Java 8 函数式编程风格

Java 迄今为止最令人激动的特征。这些新的语言特征允许采用函数式风格来进行编码,我们可以用这些特性完成许多有趣的功能。这些特性如此有趣以至于被认为是不合理的.他们说会影响计算速度,但是虽然是真的,但是存在皆合理.

所以我们摒弃缺点,研究优点.

演练

今天的新闻联播播出的主要内容有:list转map,list使用lambda求和,等聚合运算,映射分类,分组,排序,归约等算法示例 你们就静鸡鸡的看吧

------还演练了parallelStream并行流          [林凌你个鸭蛋 要是进来不点赞我就....]
 package test;

 /**
* @Title: Dog.java
* @Description: Stream的演练
* @author LiJing
* @date 2018/12/3 14:13
* @version 1.0.0
* @implNote stream的特性 我们首先列出stream的如下三点特性,在之后我们会对照着详细说明
* 1.stream不存储数据
* 2.stream不改变源数据
* 3.stream的延迟执行特性
* 通常我们在数组或集合的基础上创建stream,stream不会专门存储数据,对stream的操作也不会影响到创建它的数组和集合,
* 对于stream的聚合、消费或收集操作只能进行一次,再次操作会报错
*/ import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; import java.text.Collator;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream; @Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
private Long id;//编号
private String name;//名字
private Integer age;//年龄
private String team;//分队 public static void main(String[] args) {
List<Dog> list = Lists.newArrayList();
list.add(new Dog(5L, "林凌", 2, "M"));
list.add(new Dog(1L, "豆豆", 5, "F"));
list.add(new Dog(2L, "苗苗", 3, "a"));
list.add(new Dog(4L, "糖糖", 3, "A"));
list.add(new Dog(6L, "文文", 3, "G"));
list.add(new Dog(3L, "晴晴", 4, "g")); //提取组别号 转换大写 这里可以看到 一个 Stream 只可以使用一次 map映射有很多方法
List<String> teamList = list.stream().map(Dog::getTeam).collect(Collectors.toList());
List<String> listCase = teamList.stream().map(String::toUpperCase).collect(Collectors.toList()); /**list操作*/
//1.获取年龄最大/最小的狗
Dog dog2MaxAge = list.stream().max((d1, d2) -> d1.getAge().compareTo(d2.getAge())).orElse(new Dog());
Dog dog1MaxAge = list.stream().min(Comparator.comparing(Dog::getAge)).get(); //2.静态方法 单一操作时候可以简化为如下:
list.stream().forEach(x -> System.out.println(x));
list.forEach(System.out::println); //3.排序 根据年龄排序 默认升序
List<Dog> sorted1AgeList = list.stream().sorted(Comparator.comparing(Dog::getAge)).collect(Collectors.toList());
//同等按照id再来排序,混合排序 复合排序
List<Dog> sorted2AgeList = list.stream().sorted(Comparator.comparing(Dog::getAge).thenComparing(Dog::getId)).collect(Collectors.toList());
//降序排列
List<Dog> sorted3AgeList = list.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge()).collect(Collectors.toList());
//反转 倒序,逆序处理
List<Dog> sorted4AgeList = list.stream().sorted(Comparator.comparing(Dog::getAge).reversed()).collect(Collectors.toList()); //4.根据组队号 自然排序
List<Dog> sortedTeamList = list.stream().sorted(Comparator.comparing(Dog::getTeam)).collect(Collectors.toList());
//5.根据名字的字母 Collator 类执行区分语言环境的 String 比较
List<Dog> sortedNameList = list.stream().sorted((o1, o2) -> Collator.getInstance(Locale.CHINESE).compare(o1.getName(), o2.getName())).collect(Collectors.toList());
//6.去重 去掉重复的结果
List<Dog> distinctList = list.stream().distinct().collect(Collectors.toList());
//7.截取 截取流的前N个元素
List<Dog> limitList = list.stream().limit(3).collect(Collectors.toList());
//8.跳过流的前n个元素
List<Dog> skipList = list.stream().skip(4).collect(Collectors.toList());
//9.将小流合并成一个大流
List<String> listLine = new ArrayList<String>();
listLine.add("I am a boy");
listLine.add("I love the girl");
listLine.add("But the girl loves another girl"); //按空格分词 分完词之后,每个元素变成了一个String[]数组
Stream<String[]> stream = listLine.stream().map(line -> line.split(" "));
//将每个String[]变成流
Stream<Stream<String>> streamStream = listLine.stream().map(line -> line.split(" ")).map(Arrays::stream);
//此时一个大流里面包含了一个个小流,我们需要将这些小流合并成一个流
Stream<String> stringStream = listLine.stream().map(line -> line.split(" ")).flatMap(Arrays::stream); //找出id>3的 按照年龄排序 输出他们的名字
List<String> studentList = list.stream()
.filter(x -> x.getId() > 3)
.sorted(Comparator.comparing(Dog::getAge).reversed())
.map(Dog::getName)
.collect(Collectors.toList());
System.out.println(studentList); /**map操作*/
//1.转为Map 以编号:key 以名字为value 用::,和x->都可以 推荐::
Map<Long, String> map1 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x.getName())); //2.转为Map 以编号:key 以对象自身为value 用x->x,和Function.identity() 推荐::Function.identity()
Map<Long, Dog> map2 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x));
Map<Long, Dog> map3 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity())); //3.重复key的情况,这时候流的处理会抛出个异常:Java.lang.IllegalStateException:Duplicate key。
//这时候就要在toMap方法中指定当key冲突时key的选择。(这里是选择第二个key覆盖第一个key)
Map<Long, Dog> map4 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity(), (oldKey, newKey) -> newKey)); //4.根据 年龄or组队 分组用groupingBy 可以分组成多个列表
Map<Integer, List<Dog>> map5 = list.stream().collect(Collectors.groupingBy(Dog::getAge));
Map<String, List<Dog>> map6 = list.stream().collect(Collectors.groupingBy(Dog::getName)); //5.partitioningBy可以理解为特殊的groupingBy,key值为true和false,当然此时方法中的参数为一个判断语句(用于判断的函数式接口)
Map<Boolean, List<Dog>> map7 = list.stream().collect(Collectors.partitioningBy(o -> o.getAge() < 3)); //遍历小map
map6.forEach((k, v) -> System.out.println(k + v)); //归约 获取年龄最大的狗狗
Dog dog = list.stream().reduce((p1, p2) -> {
System.out.println("p1===" + p1);
System.out.println("p2===" + p2);
return p1.getAge() > p2.getAge() ? p1 : p2;
}).get();
System.out.println(dog); //归约 求和 计算所有狗狗的年龄总和
//reduce的第一个参数表示初试值为0;
//求和 那么可以使用Integer提供了sum函数代替自定义的Lambda表达式
int age2Total = list.stream().mapToInt(Dog::getAge).reduce(0, Integer::sum); /**聚合运算*/
//求和
long sum = list.stream().mapToLong(Dog::getId).sum();
//均值
double average = list.stream().mapToInt(Dog::getAge).average().getAsDouble();
//统计
long count = list.stream().count();
long count1 = list.stream().map(Dog::getId).count();
long count2 = list.stream().mapToInt(Dog::getAge).count(); /**匹配:*/
//如果流为空,则返回false并且不评估谓词 ==>检查所有的名字是否是以 '文'结尾的 显然不是 [少一个都不行,全检性] 看电影去:<一个都不能少>
boolean b1 = list.stream().allMatch(x -> x.getName().endsWith("文"));//false
//如果流为空,则返回false并且不评估谓词 ==>检查是否有在A组的小狗 [存在性]
boolean b2 = list.stream().anyMatch(x -> "A".compareToIgnoreCase(x.getTeam()) < 0);//true
//如果流为空,则返回false并且不评估谓词 ==>检查是否有任意小狗 年龄大于3岁 [存在性]
boolean b3 = list.stream().anyMatch(x -> x.getAge() < 3);//true
// list = Collections.EMPTY_LIST;
//如果流为空,则返回true并且不评估谓词 ==> allMatch相反,检查有么狗的年龄大于100岁的 [有一个都不行,全检性]
boolean b4 = list.stream().noneMatch(x -> x.getAge() > 100);//true //findAny list即stream是有顺序的 findAny能够从流中随便选一个元素出来
Optional<Dog> optionalDog = list.stream().findAny();//isPresent表示是否存在
System.out.println(optionalDog.isPresent()); //再看 其实就是 b6就是b7
boolean b6 = list.stream().filter(x -> x.getAge() < 3).findAny().isPresent();//true
//过滤
List<Dog> filterList1 = list.stream().filter(x -> x.getAge() < 3).collect(Collectors.toList());
boolean b7 = list.stream().anyMatch(x -> x.getAge() < 3);//true 返回在流遍历过程是否遇到过的年龄大于3的狗 //与guava的getFirst方法类似,Stream.findFirst将返回给定流的第一个元素,如果流为空,则返回空Optional。如果流没有遭遇顺序,则可以返回任何元素
Optional<Dog> first = list.stream().findFirst(); /**
* parallelStream是什么
* parallelStream其实就是一个并行执行的流.它通过默认的ForkJoinPool,可能提高你的多线程任务的速度.
* Stream具有平行处理能力,处理的过程会分而治之,也就是将一个大任务切分成多个小任务,这表示每个任务都是一个操作,因此像以下的程式片段
*
* 而可能是任意的顺序,就forEach()这个操作來讲,如果平行处理时,希望最后顺序是按照原来Stream的数据顺序,那可以调用forEachOrdered()
* 如果forEachOrdered()中间有其他如filter()的中介操作,会试着平行化处理,然后最终forEachOrdered()会以原数据顺序处理,
* 因此,使用forEachOrdered()这类的有序处理,可能会(或完全失去)失去平行化的一些优势,实际上中介操作亦有可能如此,例如sorted()方法
*
* parallelStream背后的男人:ForkJoinPool
* ForkJoinPool主要用来使用分治法(Divide-and-Conquer Algorithm)来解决问题
* 那么使用ThreadPoolExecutor或者ForkJoinPool,会有什么性能的差异呢?
* 首先,使用ForkJoinPool能够使用数量有限的线程来完成非常多的具有父子关系的任务,比如使用4个线程来完成超过200万个任务。
* 但是,使用ThreadPoolExecutor时,是不可能完成的,因为ThreadPoolExecutor中的Thread无法选择优先执行子任务,
* 需要完成200万个具有父子关系的任务时,也需要200万个线程,显然这是不可行的。
* */
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream().forEach(System.out::print);
System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一样");
numbers.stream().parallel().forEach(System.out::print);
System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一样");
numbers.stream().parallel().forEachOrdered(System.out::print);
System.out.println(); Optional<Dog> any = list.parallelStream().findAny();
System.out.println(any.isPresent()); //并行流 此时的findAny哪一个都有可能返回
Optional<Dog> any1 = list.stream().parallel().findAny();
System.out.println(any1); //生成一亿条0-100之间的记录
//感受:可见,对于串行流.distinct().sorted()方法对于运行时间没有影响,
// 但是对于串行流,会使得运行时间大大增加,因此对于包含sorted、distinct()等与全局数据相关的操作,不推荐使用并行流。
Random random = new Random();
List<Integer> integerList = Stream.generate(() -> random.nextInt(100)).limit(100000000).collect(Collectors.toList());
long begin1 = System.currentTimeMillis();
integerList.stream().filter(x -> (x > 10)).filter(x -> x < 80).count();
long end1 = System.currentTimeMillis();
System.out.println(end1 - begin1);
integerList.stream().parallel().filter(x -> (x > 10)).filter(x -> x < 80).count();
long end2 = System.currentTimeMillis();
System.out.println(end2 - end1); long begin1_ = System.currentTimeMillis();
integerList.stream().filter(x -> (x > 10)).filter(x -> x < 80).distinct().sorted().count();
long end1_ = System.currentTimeMillis();
System.out.println(end1 - begin1);
integerList.stream().parallel().filter(x -> (x > 10)).filter(x -> x < 80).distinct().sorted().count();
long end2_ = System.currentTimeMillis();
System.out.println(end2_ - end1_); //调用peek方法来瞧瞧并行流和串行流的执行顺序,peek方法顾名思义,就是偷窥流内的数据,
// peek方法声明为Stream<T> peek(Consumer<? super T> action);加入打印程序可以观察到通过流内数据,见如下代码 new Dog().peek();//串行流 线程:main:->peek2->7
new Dog().testPeekPal();//并行流 线程:ForkJoinPool.commonPool-worker-1:->peek1->4 /**总结:
*我们将stream.filter(x -> x > 5).filter(x -> x < 8).forEach(System.out::println)的过程想象成 如图-1 的管道,我们在管道上加入的peek相当于一个阀门,透过这个阀门查看流经的数据,
*1)当我们使用顺序流时,数据按照源数据的顺序依次通过管道,当一个数据被filter过滤,或者经过整个管道而输出后,第二个数据才会开始重复这一过程
*2)当我们使用并行流时,系统除了主线程外启动了七个线程(我的电脑是4核八线程)来执行处理任务,因此执行是无序的,但同一个线程内处理的数据是按顺序进行的。
* */
} public void peek() {
//生成1-10的int 流
Stream<Integer> integerStream = Stream.iterate(1, x -> x + 1).limit(10);
integerStream.peek(this::peek1).filter(x -> x > 5)
.peek(this::peek2).filter(x -> x < 8)
.peek(this::peek3)
.forEach(System.out::println);
} public void testPeekPal() {
Stream<Integer> stream = Stream.iterate(1, x -> x + 1).limit(10).parallel();
stream.peek(this::peek1).filter(x -> x > 5)
.peek(this::peek2).filter(x -> x < 8)
.peek(this::peek3)
.forEach(System.out::println);
} public void peek1(int x) {
System.out.println(Thread.currentThread().getName() + ":->peek1->" + x);
} public void peek2(int x) {
System.out.println(Thread.currentThread().getName() + ":->peek2->" + x);
} public void peek3(int x) {
System.out.println(Thread.currentThread().getName() + ":->final result->" + x);
} }

                  

图1--如上

图2--如下

 Collectors 类的静态工厂方法:

工厂方法 返回类型 作用
toList List<T> 把流中所有项目收集到一个 List
toSet Set<T> 把流中所有项目收集到一个 Set,删除重复项
toCollection Collection<T> 把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new)
counting Long 计算流中元素的个数
sumInt Integer 对流中项目的一个整数属性求和
averagingInt Double 计算流中项目 Integer 属性的平均值
summarizingInt IntSummaryStatistics 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值
joining String 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", "))
maxBy Optional<T> 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty()
minBy Optional<T> 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty()
reducing 归约操作产生的类型 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
groupingBy Map<K, List<T>> 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键
partitioningBy Map<Boolean,List<T>> 根据对流中每个项目应用谓词的结果来对项目进行分区
 List<Matchs> matchsList = new ArrayList();

 Map<String,List<Matchs>> MatchsListMap=matchsList.stream()
.collect(Collectors.groupingBy(Matchs::getMatchDate));

此时MatchsListMap的排序规则是根据MatchDate降序的(默认),也就是说map中0下标的key是最大的MatchDate值,那么如果需要根据MatchDate升序该怎么办呢?

TreeMap<String, List<Matchs>> matchsListMap = matchsList.stream()
.collect(Collectors.groupingBy(Matchs::getMatchDate,TreeMap::new,Collectors.toList()));

此时返回的为TreeMap类型数据,TreeMap默认为按照key升序,descendingMap()降序输出

最新文章

  1. D3.js学习(三)
  2. JavaScript 动态插入 CSS
  3. spring batch部分
  4. Python3.5连接Mysql
  5. Hadoop Streaming例子(python)
  6. TaskTracker任务初始化及启动task源码级分析
  7. 模拟 ZOJ 3736 Pocket Cube
  8. 匿名函数自执行原理和instanceof运算符执行原理
  9. VC项目配置基础以及快捷键(收藏)
  10. Sublime Text各种插件使用方法
  11. php字符串处理函数常见问题
  12. android上下文
  13. django 新闻编辑笔记
  14. HDU ACM 1054 Strategic Game 二分图最小顶点覆盖?树形DP
  15. Apple Watch 1.0 开发介绍 1.3 简介 WatchKit App 架构
  16. java volatitle介绍与使用
  17. package.json 里的 dependencies和devDependencies区别
  18. BZOJ5465 : [APIO 2018] 选圆圈
  19. 输入法设置,SublimeTest,putty掉线
  20. getDimension与getDimensionPixelOffset与getDimensionPixelSize的区别

热门文章

  1. bs4源码
  2. Kubernetes dashboard安装
  3. 解决jQuery和其他库冲突
  4. leetcode894
  5. App专项测试之弱网测试
  6. Android App专项测试(压力测试)
  7. element-ui:el-table时间格式化
  8. CSS设计中的错误大整理!
  9. javaScript:压缩图片并上传
  10. PromiseThen 横向代码编辑