前面我们讲了
Lambda
表达式与Optional
类,下面我们将会使用这两个新特性,特别是Lambda
。
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列集合讲的是数据,Stream讲的是计算!
注意:
- Stream自己不会存储元素
- Stream不会改变源对象。相反,他们会返回一个特有结果的新Stream
- Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
实际开发中,项目多数数据源都来自于
Mysql,Oracle等
。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理Stream和Collection集合的区别:==Collectioin是一种静态的内存数据结构,而Stream是有关计算的。==前者主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算。
假设你有一个班所有学生的分数数据,你需要从中筛选出成绩不及格的,并且按照分数排序,打印按顺序学生的名称
package com.tcc.test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;/*** @author 宇辰* @date 2022/8/31-8:53**/
public class Test{public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 56D),new Student("李四", 23, "101班", 44D),new Student("王五", 25, "102班", 44.5D));ArrayList list = new ArrayList<>();// 筛选出所有成绩不合格的学生for (Student s : students) {if (s.getScore() < 60){list.add(s);}}// 排序Comparator com = new Comparator() {@Overridepublic int compare(Object o1, Object o2) {Student stu1 = (Student) o1;Student stu2 = (Student) o2;// 按照分数排序return stu1.getScore().compareTo(stu2.getScore());}};list.sort(com);// 遍历得出所有名称/*李四王五张三*/for (Student s : list) {System.out.println(s.getName());}}}class Student{// 姓名private String name;// 年龄private Integer age;// 班级private String clazz;// 分数private Double score;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getClazz() {return clazz;}public void setClazz(String clazz) {this.clazz = clazz;}public Double getScore() {return score;}public void setScore(Double score) {this.score = score;}public Student() {}public Student(String name, Integer age, String clazz, Double score) {this.name = name;this.age = age;this.clazz = clazz;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", clazz='" + clazz + '\'' +", score=" + score +'}';}
}
public class Test{public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 56D),new Student("李四", 23, "101班", 44D),new Student("王五", 25, "102班", 44.5D));/*李四王五张三*/students.stream()// 中间操作,过滤小于60分的同学.filter(s -> s.getScore() < 60)// 中间操作,定制排序.sorted((s1,s2) -> Double.compare(s1.getScore(),s2.getScore()))// 中间操作,依次获取学生的姓名.map(s -> s.getName())// 终止操作,内部迭代打印获取到的每个学生的姓名.forEach(System.out::println);}}....省略Student类
- Java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个是Stream API
- ==Stream API(java.util.stream)==把真正的函数式编程风格引入到了Java中。这是目前为止对Java类库最好的补充,因为StreamAPI可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
- Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非重复杂的查找、过滤和映射数据等操作。使用==Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。==也可以使用Stream API来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式
一个数据源(如:集合、数组),获取一个流
一个中间操作链,对数据源的数据进行处理
一旦执行终止操作,就执行中间操作链,并产生结果:之后,不会再被使用
并行流:就是把一个内容分成多个数据块,并用不用的线程分别处理每个数据块的。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。
StreamAPI可以声明性地通过
parallel()
与sequential()
在并行流与顺序流之间进行切换
以下所有演示代码都使用了Lambda表达式,如果不了解的话,可以阅读:Lamdba表达式快速学习与使用
Java8 中的Collection接口被扩展,提供了两个获取流的方法:
- default Stream stream():返回一个顺序流
- default Stream parallelStream():返回一个并行流
public static void main(String[] args){ArrayList list = new ArrayList<>();// 创建一个串行流Stream stream = list.stream();// 创建一个并行流Stream parallelStream = list.parallelStream();
}
Java8 中的Arrays 的静态方法stream()可以获取数组流:
- seatic Stream stream(T[] array):返回一个流
重载形式,能够处理对应基本类型的数组:
- public static IntStream stream(int[] array)
- public static LongStream stream(logn[] array)
- public static DoubleStream stream(double[] array)
public static void main(String[] args){String[] s = new String[16];// 串行流,没有提供创建并行流的方法Stream str1 = Arrays.stream(s);
}
可以调用Stream类静态方法of(),通过显示值创建一个流。它可以接收任意数量的参数
- public static Stream of(T… values):返回一个流
public static void main(String[] args){Stream stream = Stream.of(123, 222, 344);
}
Java中用于处理文件等I/O操作的NIO API(非阻塞I/O)已更新,以便利用Stream API。java.nio.file.Files中的很多静态方法都会返回一个流。例如,一个很有用的方法:
Files.lines
,它会返回一个由指定文件中的各行构成的字符串流。例子:差异一个文件中有多少各不相同的词
public static void main(String[] args){// 这样写会自动关闭流try (Stream lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());){// 需要用flatMap方法扁平化返回值lines.flatMap(line -> Arrays.stream(line.split(" ")))// 中间操作,去重.distinct()// 终止操作,计数.count();}catch (IOException e){e.printStackTrace();}
}
可以使用静态方法Stream.iterate()和Stream.generate(),创建无限流。
- 迭代
- public static Stream iterate(final T seed,final UnaryOperator f)
- 生成
- public static Stream generate(Supplier s)
public static void main(String[] args){Stream iterate = Stream.iterate(0, t -> t + 2);// 中间操作,后面会讲到,用于截断流,取前三个Stream limit = iterate.limit(3);/*024*/// 终止操作 内部迭代limit.forEach(System.out::println);
}
public static void main(String[] args){Stream generate = Stream.generate(Math::random);Stream limit = generate.limit(3);/*0.491710149749352570.70731851158800650.5253605859404055*/limit.forEach(System.out::println);
}
多个
中间操作
可以连接起来形成一个流水线,除非流水线
上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,成为"惰性求值"
- filter:接收Lambda,从流汇总排除某些元素
- distinct():筛选,通过流所生成元素的
hashCode()和equals()
去除重复元素- limit(long maxSize):截断流,使其元素不超过给定数量
- skip(long n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流
比如说:你想获取所有成绩不及格的学生
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 如果满足条件,就继续执行。不满足条件的剔除.filter(s -> s.getScore() < 60)/*Student{name='李四', age=21, clazz='101班', score=50.0}Student{name='王五', age=22, clazz='102班', score=44.5}Student{name='赵六', age=20, clazz='103班', score=44.5}*/.forEach(System.out::println);
}// ...Student类定义省略,前言第二部分举例里面有。。
去重,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流
比如说:你想知道同学的年龄都在哪个年龄段上有
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 获取所有同学的年龄.map(Student::getAge)// 去重.distinct()/*202122*/.forEach(System.out::println);
}// ...Student类定义省略,前言第二部分举例里面有。。
截短流,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。
如果流式有序的,则最多会返回前n个元素。
比如说:你想获取成绩不及格的前两个人
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 如果满足条件,就继续执行。不满足条件的剔除.filter(s -> s.getScore() < 60).limit(2)/*Student{name='李四', age=21, clazz='101班', score=50.0}Student{name='王五', age=22, clazz='102班', score=44.5}*/.forEach(System.out::println);
}// ...Student类定义省略,前言第二部分举例里面有。。
流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。
注意:limit(n)和skip(n)是互补的:
比如说:你想获取除了前两个后面的所有不及格的学生
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 如果满足条件,就继续执行。不满足条件的剔除.filter(s -> s.getScore() < 60).skip(2)// Student{name='赵六', age=20, clazz='103班', score=44.5}.forEach(System.out::println);
}
- map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
- mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream
- mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream
- mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream
流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一次,是因为它和转换类似,但其中的细微差别在于,它是"创建一个新的版本",而不是去"修改"原来的数据)
比如说:你想获取所有学生的姓名
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().map(Student::getName)/*李四王五赵六田七*/.forEach(System.out::println);
}
比如说:你想再获取每个人名字有多长
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().map(Student::getName).map(String::length)/*2222*/.forEach(System.out::println);
}
flatMap方法让你把一个流中的每个值都换成另一个流,然后把所有流链接起来成为一个流
比如说:你想获取所有人名字不相同的字有哪些,我们先来使用map看看会有什么问题
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("张四", 21, "101班", 50D),new Student("李三", 20, "103班", 44.5D));// 你会发现它返回的不是你想要的Stream,而是里面包含了一层String[]Stream stream = students.stream().map(Student::getName).map(str -> str.split("")).distinct();// 这时候你需要在forEach里面再遍历strings数组,并没有达到去重的效果/*张三张四李三*/stream.forEach(strings -> {for (String s : strings) {System.out.println(s);}});
}
我们可以使用flatMap来简化上面代码
- 我们使用split方法获取到的是一个数组,我们上面学习到,可以使用
Arrays.stream()
方法,把数组转换为一个Stream流- 转换为流后,我们的返回值就会为
Stream
,这时候我们使用flatMap方法,把这个流中流,转换为单个流> Stream
实践如下
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("张四", 21, "101班", 50D),new Student("李三", 20, "103班", 44.5D));students.stream().map(Student::getName).flatMap(str -> Arrays.stream(str.split(""))).distinct()/*张三四李*/.forEach(System.out::println);
}
想IntSteam、DoubleStream、LongStream都属于数值流,我们会放到终止操作的第二章(归约)后面详细讲解
- sorted():产生一个新流,其中按自然顺序排序
- sorted(Comparator com):产生一个新流,其中按比较器顺序排序
根据元素类型内置的排序规则进行排序,默认升序排序
比如说:根据每个学生的分数进行排序
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().map(Student::getScore)// 根据Double类内置的compareTo方法进行排序.sorted()/*44.544.550.088.091.0*/.forEach(System.out::println);
}
根据自定义排序规则进行排序
比如说:根据每个学生分数进行排序,如果分数相同,则根据年龄排序
// 元数据
List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D)
);
students.stream().sorted((s1,s2) -> {int i = s1.getScore().compareTo(s2.getScore());if(i == 0){// 如果需要降序排序,前面加个 - 号即可return s1.getAge().compareTo(s2.getAge());}// 如果需要降序排序,前面加个 - 号即可return i;})/*Student{name='赵六', age=20, clazz='103班', score=44.5}Student{name='王五', age=22, clazz='102班', score=44.5}Student{name='李四', age=21, clazz='101班', score=50.0}Student{name='张三', age=20, clazz='100班', score=88.0}Student{name='田七', age=21, clazz='103班', score=91.0}*/.forEach(System.out::println);
}
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。
流进行了终止操作后,不能再次使用。可以再次生成一个新流进行使用
- allMatch(Predicate p):检查是否匹配所有元素
- anyMatch(Predicate p):检查是否至少匹配一个元素
- noneMatch(Predicate p):检查是否没有匹配所有元素
- findFirst():返回第一个元素
- findAny():返回当前流中的任意元素
- count():返回流中元素总数
- max(Comparator c):返回流中最大值
- min(Comparator c):返回流中最小值
- forEach(Consumer c):内部迭代(使用Collection接口需要用户去做迭代,成为
外部迭代
。相反,StreamAPI使用内部迭代———它帮你把迭代做了)
检测流中所有元素是否都能匹配给定的谓词
比如说:你想查看是否所有学生成绩都及格了
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));boolean b = students.stream().allMatch(s -> s.getScore() >= 60);System.out.println(b); // false
}
检测流中是否有一个元素能匹配给定的谓词
比如说:你想查看是否有一个同学不及格
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));boolean b = students.stream().anyMatch(s -> s.getScore() < 60);System.out.println(b); // true
}
与
allMatch
相反,检测流中所有元素是否都不能匹配给定的谓词比如说:你想看看是否所有的学生都不是105班的
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));boolean b = students.stream().noneMatch(s -> "105".equals(s.getClazz()));System.out.println(b); // true
}
有些流使用排序来指定集合中的顺序,对于这种元素,你可能想要找到第一个元素。
比如说:找到分数第一的学生
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional student = students.parallelStream()// 使用Comparator的默认方法,reversed方法用于反转顺序,默认升序排序,需要降序排序,然后拿到第一名的学生.sorted(Comparator.comparingDouble(Student::getScore).reversed()).findFirst();System.out.println(student); // Optional[Student{name='田七', age=21, clazz='103班', score=91.0}]
}
findAny方法将返回当前流中任意元素。可以与其他流操作结合使用
比如说:你可能想抽取103班的任意一人
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常// 使用并行流可以直观看出(多运行几次,查看结果)Optional student = students.parallelStream().filter(s -> "103班".equals(s.getClazz())).findAny();System.out.println(student); // Optional[Student{name='赵六', age=20, clazz='103班', score=44.5}]}
比如说:你想知道有几个学生不及格
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));long count = students.stream().filter(s -> s.getScore() < 60).count();System.out.println(count); // 3
}
比如说:你想知道最高分是多少
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 从1-100的所有偶数个数Optional max = students.stream().map(Student::getScore).max(Double::compare);System.out.println(max); // Optional[91.0]
}
比如说:你想知道最低分是多少
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 从1-100的所有偶数个数Optional max = students.stream().map(Student::getScore).min(Double::compare);System.out.println(max); // Optional[44.5]
}
比如说:你想打印成绩不及格的学生姓名
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().filter(s -> s.getScore() < 60)/*李四王五赵六*/.forEach(s -> System.out.println(s.getName()));
}
- reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回T
- 参数一:变量的初始值
- 参数二:将列表中所有元素结合在一起的操作
- reduce(BinaryOperator b):可以将流中元素反复结合起来,的达到一个值。返回Optional
map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名
总和:比如说:你想知到所有学生的总分是多少
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Double reduce = students.stream()// 获取所有同学的分数.map(Student::getScore)// .reduce(0D, (a, b) -> a + b);// 优化,Double有一个sum的静态方法,恰巧可以让我们使用方法引用.reduce(0D,Double::sum);System.out.println(reduce); // 318.0
}
最大值:比如说:你想知道最高分是多少
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional reduce = students.stream().map(Student::getScore)// .reduce((a, b) -> a>b?a:b);// 优化,Double有一个max的静态方法,恰巧可以让我们使用方法引用.reduce(Double::max);System.out.println(reduce); // Optional[91.0]
}
最小值:比如说:你想知道最低分是多少
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional reduce = students.stream().map(Student::getScore)
// .reduce((a, b) -> a>b?b:a);// 优化,Double有一个min的静态方法,恰巧可以让我们使用方法引用.reduce(Double::min);System.out.println(reduce); // Optional[44.5]}
为什么这个方法的返回值会是Optional类型的呢,考虑到流中没有任何元素的情况,reduce无法返回其和,因为它没有初始值
比如说:你想知到所有学生的总分是多少
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional reduce = students.stream().map(Student::getScore)// .reduce((a, b) -> a + b);// 优化,Double有一个sum的静态方法,恰巧可以让我们使用方法引用.reduce(Double::sum);System.out.println(reduce); // Optional[318.0]
}
经过上面的归约计算求和,我们回想,如果直接有一个sum()的终端操作,不是更好,就不需要调用Double里面的方法了。
但这是不可能的。问题在于map方法会生成一个Stream。虽然流中的元素是Double类型,但Stream接口没有定义sum方法。
为什么没有呢?比方说你只有一个像Student那样的Stream,把每个学生加起来是没有任何意义的。
但是不要担心,Stream API还提过了原始类型流特化,专门支持处理数值流的方法
原始类型流特化:
Java 8引入了三个原始类型流特化接口来解决这个问题,从而不面了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max等…
- 映射到数值流
- mapToInt:将
Stream
类型转化为IntStream
类型,执行专属的操作- mapToDouble:将
Stream
类型转化为DoubleStream
类型,执行专属的操作- mapToLong:将
Stream
类型转化为LongStream
类型,执行专属的操作- 转换回对象流:一旦有了数值流,你可能会想把它转换回非特化流
- boxed:将原始流转换为一般流
这里只讲解这一个,因为其他两个使用方法和这个一模一样,只是操作的数据类型不一致而已
比如说:在这里把上面使用
reduce
进行的求和、最大值、最小值再次优化一遍,并且再获取所有学生的平均分
- 这里,你会看到
OptionalDouble
类,这是对于Optional
原始类型的特化版本,有OptionalInt、OptionalDouble、OptionalLong
三个。因为求和的例子很容易,因为它有一个默认值:0.但是,如果要计算IntStream中的最大元素,就得换个贩子了,因为0是错误的结果。
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 分数总和DoubleStream doubleStream = students.stream().mapToDouble(Student::getScore);double sum = doubleStream.sum();System.out.println(sum); // 318.0// 最高分OptionalDouble max = students.stream().mapToDouble(Student::getScore).max();System.out.println(max); // OptionalDouble[91.0]// 最低分OptionalDouble min = students.stream().mapToDouble(Student::getScore).min();System.out.println(min); // OptionalDouble[44.5]// 平均分OptionalDouble average = students.stream().mapToDouble(Student::getScore).average();System.out.println(average); // OptionalDouble[63.6]
}
// 分数总和
DoubleStream doubleStream = students.stream().mapToDouble(Student::getScore);
Stream boxed = doubleStream.boxed();
和数字打交道时,有一个常用的东西就是数值范围。比如,假设你想要生成1和100之间的所有数字。Java8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed。这两个方法都是第一个参数接收起始值,第二个参数接受结束值。但range是不包含结束值的,而rangeClosed则包含结束值。
比如说:你想知道1-100的所有偶数的个数
// 从1-100的所有偶数个数 如果是range则49个
long count = IntStream.rangeClosed(1, 100).filter(i -> i % 2 == 0).count();
System.out.println(count); // 50
- collect(Collector c):将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)
另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法如下:
- List toList:把流中元素收集到List
- Set toSet:把流中元素收集到Set
- Collection toCollection:把流中元素收集到创建的集合
- Long counting:计算流中的元素的个数
- Integer/Long/Double summingInt/summingLong/summingDouble:对流中元素的整数属性求和
- Double:averagingInt/averagingLong/averagingDouble:计算流中元素Integer属性的平均值
- xxxSummaryStatistics summarizingInt/summarizingLong/summarizingDouble:收集流中Integer属性的统计值。如:平均值
- String joining:连接流中每个字符串
- Optional maxBy:根据比较器选择最大值
- Optional minBy:根据比较器选择最小值
- 归约产生的类型 reducing:从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
- 转换函数返回的类型 collectingAndThen:包裹另一个收集器,对其结果转换函数
- Map
groupingBy :根据某属性值对流分组,属性为K,结果为V- Map
partitioningBy :根据true或false进行分区
将流中所有元素都放进List里面,并返回这个List
比如说:你想获取所有的学生姓名,并放进List里面
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法ArrayList stuName = new ArrayList<>();for (Student student : students) {stuName.add(student.getName());}System.out.println(stuName); // 张三、李四、王五、赵六、田七// 1.8写法List stuNames = students.stream().map(Student::getName).collect(Collectors.toList());System.out.println(stuNames); // 张三、李四、王五、赵六、田七
}
将流中所有元素都放进Set里面,并返回这个Set集合。使用这个集合也可以进行去重操作
比如说:你想获取所有学生的年龄,并且去重(上面写过相同的案例,使用distinct),最后放到Set集合里
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashSet stuAge = new HashSet<>();for (Student student : students) {stuName.add(student.getAge());}System.out.println(stuAge); // 20、21、22// 1.8写法Set stuAges = students.stream().map(Student::getAge).collect(Collectors.toSet());System.out.println(stuAges); // 20、21、22
}
参数为:
Supplier
类型collectionFactory 依旧是上面的需求,只不过需要放到LinkedList里面
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashSet stuAge = new HashSet<>();for (Student student : students) {stuAge.add(student.getAge());}LinkedList stuAge2 = new LinkedList<>();for (Integer integer : stuAge) {stuAge2.add(integer);}System.out.println(stuAge2); // 20、21、22// 1.8写法LinkedList stuAges = students.stream().map(Student::getAge).distinct().collect(Collectors.toCollection(LinkedList::new));System.out.println(stuAges); // 20、21、22
}
对流中元素进行计数(与上面讲解的
count终端操作
功能一样)比如你需要统计不及格的总共有多少个学生
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法int num = 0;for (Student student : students) {if (student.getScore() < 60){num ++;}}System.out.println(num); // 3// 1.8写法Long collects = students.stream().filter(s -> s.getScore() < 60).collect(Collectors.counting());System.out.println(collects); // 3
}
参数为:
ToIntFunction super T>
类型,对流中所有Integer类型的数据求和对数值类型操作,还可以使用上方讲解的特化流进行操作。
比如说:你想获取所有学生的年龄之和
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Integer num = 0;for (Student student : students) {num += student.getAge();}System.out.println(num); // 104// 1.8写法Integer collects = students.stream().collect(Collectors.summingInt(Student::getAge));System.out.println(collects); // 104
}
对Integer类型的数据求平均值
比如说:你想知道所有学生年龄和分数的平均值
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Double num = 0D;int count = 0;int anum = 0;for (Student student : students) {count ++;num += student.getScore();anum += student.getAge();}// 分数double avg = num / count;System.out.println(avg); // 63.6// 年龄 int / int类型,返回值为int类型的话,会丢失精度Double age = Double.valueOf(anum / count);System.out.println(age); // 20.8// 1.8写法// 分数Double avgs = students.stream().collect(Collectors.averagingDouble(Student::getScore));System.out.println(avgs); // 104// 年龄Double ages = students.stream().collect(Collectors.averagingInt(Student::getAge));System.out.println(ages); // 20.8
}
统计Integer类型的所有统计值,比如:平均值、求和、最大值、最小值、计数,然后把所有结果放到``类型里面
比如说,你想知道这次考试,共有多少名学生参加了、最高分、最低分、所有学生的分均分,所有学生的总分为多少,这时候,按照上面的写法,你就需要为每个结果创建一个流,然后调用Collectors里面的不同的计算方法。Java为我们提供了一个总的计算方法,可以计算你所有需求的统计
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法,这里就不写了,太多了。。。// 1.8写法DoubleSummaryStatistics collect = students.stream().collect(Collectors.summarizingDouble(Student::getScore));// DoubleSummaryStatistics{count=5, sum=318.000000, min=44.500000, average=63.600000, max=91.000000}System.out.println(collect);// 最高分System.out.println(collect.getMax()); // 91.0// 最低分System.out.println(collect.getMin()); // 44.5// 平均值System.out.println(collect.getAverage()); // 63.6// 总和System.out.println(collect.getSum()); // 318.0// 总数System.out.println(collect.getCount()); // 5
}
joining方法返回的收集器会把流中每个对象应用toString方法得到的所有字符串连接成一个字符串,内部使用了StringBuilder把生成的字符串逐个追加起来。如果Student类中有一个toString方法来返回学生的名称,则无需映射每个学生的名称就能得到相同的结果。
- joining():直接拼接,没有拼接符
- joining(CharSequence delimiter):中间使用指定拼接符进行拼接
比如说:你想把每个学生名称拼接起来
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法String name = "";for (Student student : students) {name += student.getName();}System.out.println(name); // 张三李四王五赵六田七// 1.8写法// joiningString collect = students.stream().map(Student::getName).collect(Collectors.joining());System.out.println(collect); // 张三李四王五赵六田七// joining(CharSequence delimiter)String collect2 = students.stream().map(Student::getName).collect(Collectors.joining(","));System.out.println(collect2); // 张三,李四,王五,赵六,田七
}
根据自定义排序规则,进行定制排序,获取最大值
比如说:你想获取最高分学生的信息
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Comparator comparator = new Comparator() {@Overridepublic int compare(Object o1, Object o2) {Student s1 = (Student)o1;Student s2 = (Student)o2;return -s1.getScore().compareTo(s2.getScore());}};students.sort(comparator);System.out.println(students.get(0)); // Student{name='田七', age=21, clazz='103班', score=91.0}// 1.8写法// joiningOptional collect = students.stream().collect(Collectors.maxBy(Comparator.comparingDouble(Student::getScore)));System.out.println(collect); // Optional[Student{name='田七', age=21, clazz='103班', score=91.0}]
}
根据自定义排序规则,进行定制排序,获取最小值
比如说:你想获取最低分学生的信息
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Comparator comparator = new Comparator() {@Overridepublic int compare(Object o1, Object o2) {Student s1 = (Student)o1;Student s2 = (Student)o2;return s1.getScore().compareTo(s2.getScore());}};students.sort(comparator);System.out.println(students.get(0)); // Student{name='王五', age=22, clazz='102班', score=44.5}// 1.8写法// joiningOptional collect = students.stream().collect(Collectors.minBy(Comparator.comparingDouble(Student::getScore)));System.out.println(collect); // Optional[Student{name='王五', age=22, clazz='102班', score=44.5}]
}
有三个重载方法
- Collector
reducing(T identity, BinaryOperator op): - Collector
reducing(BinaryOperator op): - Collector
reducing(U identity, Function super T, ? extends U> mapper,BinaryOperator op)
- 第一个参数是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值和而言,0是一个合适的值
- 第二个参数相当于是map方法,将Student类中的用于计算的数值提取出来
- 第三个参数是一个BinaryOperator,将两个项目累计成一个同类型的值。这里就是对Double类型进行求和
跟上面讲的
reduce
归约基本一样,只有参数有差异比如说:你想获取所有学生的总分
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法// 1.8写法Double collect1 = students.stream().collect(Collectors.reducing(0D, Student::getScore, Double::sum));System.out.println(collect1); // 318.0Optional collect2 = students.stream().map(Student::getScore).collect(Collectors.reducing(Double::sum));System.out.println(collect2); // Optional[318.0]Double aDouble = new Double(82D);Double collect3 = students.stream().map(Student::getScore).collect(Collectors.reducing(aDouble, Double::sum));System.out.println(collect3); // 400.0
}
包裹另一个收集器,对其结果转换函数
比如说,你想使用上面第二种写法,但是又不想自己单独拆掉Optional的包装,可以使用如下方法
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法// 1.8写法Double collect = students.stream().map(Student::getScore).collect(Collectors.collectingAndThen(Collectors.reducing(Double::sum), Optional::get));System.out.println(collect); // 318.0
}
分组(重要)
根据某属性值对流分组,属性为K结果为V
- groupingBy(Function super T, ? extends K> classifier):根据指定数据进行分组
- groupingBy(Function super T, ? extends K> classifier, Collector super T, A, D> downstream):分好组后,可以对分完组的List进行其他操作,可以通过该方法,对分好组的集合进行统计、取最大值、最小值等操作
- groupingBy(Function super T, ? extends K> classifier, Supplier mapFactory, Collector super T, A, D> downstream):不晓得,很少用。。。
- 可以进行单级分组,
- 也可以进行多级分组
groupingBy(Function super T, ? extends K> classifier)
比如说,你想按照班级对各个班的学生进行分组
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashMap> map = new HashMap<>();ArrayList list = new ArrayList<>();for (Student student : students) {String clazz = student.getClazz();List l = map.get(clazz);// 如果分组不存在,则创建一个新分组if (l == null){ArrayList lis = new ArrayList<>();lis.add(student);map.put(clazz,lis);continue;}l.add(student);}/*key:100班value:[Student{name='张三', age=20, clazz='100班', score=88.0}]key:101班value:[Student{name='李四', age=21, clazz='101班', score=50.0}]key:102班value:[Student{name='王五', age=22, clazz='102班', score=44.5}]key:103班value:[Student{name='赵六', age=20, clazz='103班', score=44.5}, Student{name='田七', age=21, clazz='103班', score=91.0}]*/Set>> entries = map.entrySet();for (Map.Entry> entry : entries) {System.out.println("key:"+ entry.getKey());System.out.println("value:"+ entry.getValue());}System.out.println("******************************************");// 1.8写法Map> collect = students.stream().collect(Collectors.groupingBy(Student::getClazz));Set>> entries2 = collect.entrySet();/*key:100班value:[Student{name='张三', age=20, clazz='100班', score=88.0}]key:101班value:[Student{name='李四', age=21, clazz='101班', score=50.0}]key:102班value:[Student{name='王五', age=22, clazz='102班', score=44.5}]key:103班value:[Student{name='赵六', age=20, clazz='103班', score=44.5}, Student{name='田七', age=21, clazz='103班', score=91.0}]*/for (Map.Entry> entry : entries2) {System.out.println("key:"+ entry.getKey());System.out.println("value:"+ entry.getValue());}
}
groupingBy(Function super T, ? extends K> classifier, Collector super T, A, D> downstream)
比如说:你想统计每个班的人数
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashMap> map = new HashMap<>();ArrayList list = new ArrayList<>();for (Student student : students) {String clazz = student.getClazz();List l = map.get(clazz);// 如果分组不存在,则创建一个新分组if (l == null){ArrayList lis = new ArrayList<>();lis.add(student);map.put(clazz,lis);continue;}l.add(student);}Set>> entries = map.entrySet();HashMap map2 = new HashMap<>();for (Map.Entry> entry : entries) {map2.put(entry.getKey(),entry.getValue().size());}System.out.println(map2); // {100班=1, 101班=1, 102班=1, 103班=2}System.out.println("******************************************");// 1.8写法Map collect = students.stream().collect(Collectors.groupingBy(Student::getClazz, Collectors.counting()));System.out.println(collect); // {100班=1, 101班=1, 102班=1, 103班=2}
}
groupingBy(Function super T, ? extends K> classifier, Collector super T, A, D> downstream)
groupBy方法里面可以嵌套groupBy,进行多级分组
比如说:你想对班级分完组后,对合格和不合格的学生分下组···
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "100班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D),new Student("小八", 21, "103班", 61D));// 1.7及之前写法(如果不想了解的可以直接看下面1.8写法)HashMap>> clazzMap = new HashMap<>();for (Student student : students) {Map> clazzMap2 = clazzMap.get(student.getClazz());// 判断外部map是否分组if(clazzMap2 == null){HashMap> cMap = new HashMap<>();clazzMap.put(student.getClazz(),cMap);}// 记录当前学生成绩状态String isHeGe = "";if(student.getScore() > 60){isHeGe = "及格";}else {isHeGe = "不及格";}// 重新获取分组clazzMap2 = clazzMap.get(student.getClazz());// 判断内部map是否分组List isPassList = clazzMap2.get(isHeGe);if (isPassList == null){ArrayList sList = new ArrayList<>();sList.add(student);clazzMap2.put(isHeGe,sList);continue;}isPassList.add(student);}/*班级:100班不及格:[Student{name='李四', age=21, clazz='100班', score=50.0}]及格:[Student{name='张三', age=20, clazz='100班', score=88.0}]--------------------班级:102班不及格:[Student{name='王五', age=22, clazz='102班', score=44.5}]--------------------班级:103班不及格:[Student{name='赵六', age=20, clazz='103班', score=44.5}]及格:[Student{name='田七', age=21, clazz='103班', score=91.0}, Student{name='小八', age=21, clazz='103班', score=61.0}]*/Set>>> entrySet = clazzMap.entrySet();entrySet.forEach(m -> {System.out.println("--------------------");System.out.println("班级:" + m.getKey());Map> value = m.getValue();Set>> entries = value.entrySet();entries.forEach(map -> {System.out.println(map.getKey() + ":" + map.getValue());});});System.out.println("******************************************");// 1.8写法Map>> collect = students.stream().collect(Collectors.groupingBy(Student::getClazz, Collectors.groupingBy(s -> {if (s.getScore() >= 60) {return "及格";} else {return "不及格";}})));Set>>> entries2 = collect.entrySet();/*班级:100班不及格:[Student{name='李四', age=21, clazz='100班', score=50.0}]及格:[Student{name='张三', age=20, clazz='100班', score=88.0}]--------------------班级:102班不及格:[Student{name='王五', age=22, clazz='102班', score=44.5}]--------------------班级:103班不及格:[Student{name='赵六', age=20, clazz='103班', score=44.5}]及格:[Student{name='田七', age=21, clazz='103班', score=91.0}, Student{name='小八', age=21, clazz='103班', score=61.0}]*/entries2.forEach(m -> {System.out.println("--------------------");System.out.println("班级:" + m.getKey());Map> value = m.getValue();Set>> entries = value.entrySet();entries.forEach(map -> {System.out.println(map.getKey() + ":" + map.getValue());});});
}
分区
分区的好处在于保留了分区函数返回
true
或false
的两套流元素列表,分区的依据一定要是布尔类型
- partitioningBy(Predicate super T> predicate):按照指定的条件分组
- partitioningBy(Predicate super T> predicate, Collector super T, A, D> downstream):可以对分好的组再次进行操作,可以通过该方法,对分好组的集合进行统计、取最大值、最小值等操作
这里我们在Student类里面新加一个属性,并重新生成有参构造和getter/setter方法
// 是否在校 private Boolean isAtSchool;
partitioningBy(Predicate super T> predicate)
比如说:你想根据学生是否在校分成两个区域,一个在校的所有学生,一个不在校的学生
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D,true),new Student("李四", 21, "100班", 50D,true),new Student("王五", 22, "102班", 44.5D,false),new Student("赵六", 20, "103班", 44.5D,true),new Student("田七", 21, "103班", 91D,false),new Student("小八", 21, "103班", 61D,false));// 1.7及之前写法HashMap> map = new HashMap<>();for (Student student : students) {Boolean atSchool = false;// 先获取学生是否在校if (student.getAtSchool()){atSchool = true;}List list = map.get(atSchool);// 如果没有创建过分组if(list == null){ArrayList l = new ArrayList<>();l.add(student);map.put(atSchool,l);continue;}// 创建过分组list.add(student);}Set>> entries1 = map.entrySet();/*key:falsevalue:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}, Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false}, Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]key:truevalue:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}, Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true}, Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]*/entries1.forEach(entry -> {System.out.println("key:" + entry.getKey());System.out.println("value:" + entry.getValue());});System.out.println("******************************************");// 1.8写法Map> collect = students.stream().collect(Collectors.partitioningBy(Student::getAtSchool));Set>> entries = collect.entrySet();/*key:falsevalue:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false},Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false},Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]key:truevalue:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true},Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true},Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]*/entries.forEach(entry -> {System.out.println("key:" + entry.getKey());System.out.println("value:" + entry.getValue());});
}
partitioningBy(Predicate super T> predicate, Collector super T, A, D> downstream)
比如说:你想知道在校生和不在校生的人数
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D,true),new Student("李四", 21, "100班", 50D,true),new Student("王五", 22, "102班", 44.5D,false),new Student("赵六", 20, "103班", 44.5D,true),new Student("田七", 21, "103班", 91D,false),new Student("小八", 21, "103班", 61D,false));// 1.7及之前写法HashMap> map = new HashMap<>();for (Student student : students) {// 先获取学生是否在校Boolean atSchool = student.getAtSchool();// 尝试获取分组List list = map.get(atSchool);// 如果没有创建过分组if(list == null){ArrayList l = new ArrayList<>();l.add(student);map.put(atSchool,l);continue;}// 创建过分组list.add(student);}Set>> entries1 = map.entrySet();entries1.forEach(entry -> {/*false=3true=3*/System.out.println(entry.getKey() + "=" + entry.getValue().size());});System.out.println("******************************************");// 1.8写法Map collect = students.stream().collect(Collectors.partitioningBy(Student::getAtSchool, Collectors.counting()));System.out.println(collect); // {false=3, true=3}
}
partitioningBy(Predicate super T> predicate, Collector super T, A, D> downstream)
比如说:你想对是否在校的学生分完区后,再根据成绩是否合格进行分区显示
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D,true),new Student("李四", 21, "100班", 50D,true),new Student("王五", 22, "102班", 44.5D,false),new Student("赵六", 20, "103班", 44.5D,true),new Student("田七", 21, "103班", 91D,false),new Student("小八", 21, "103班", 61D,false));// 1.7及之前写法HashMap>> map = new HashMap<>();for (Student student : students) {Boolean atSchool = student.getAtSchool();Map> atSchoolGroupMap = map.get(atSchool);// 如果没有根据是否在校分过组if (atSchoolGroupMap == null){// 创建一个空的内map分组(根据是否合格分组)HashMap> map1 = new HashMap<>();map.put(atSchool,map1);}// 获取当前学生是否合格Boolean isHeGe = false;if (student.getScore() > 60){isHeGe = true;}// 重新获取mapMap> atSchoolGroupMap2 = map.get(atSchool);List stu = atSchoolGroupMap2.get(isHeGe);// 如果为null,说明没有创建学生是否合格的分组// 就创建一个集合,把当前新建的分组添加到内map中if (stu == null) {ArrayList list = new ArrayList<>();list.add(student);atSchoolGroupMap2.put(isHeGe,list);// 当前数据处理完毕,处理下一条continue;}// 如果有集合了,就直接添加进去即可stu.add(student);}Set>>> entries1 = map.entrySet();/*key:false成绩是否合格:false结果:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}]成绩是否合格:true结果:[Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false}, Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]----------------------------------------key:true成绩是否合格:false结果:[Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true}, Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]成绩是否合格:true结果:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}]*/entries1.forEach(entrie -> {System.out.println("----------------------------------------");System.out.println("key:" + entrie.getKey());Map> value = entrie.getValue();Set>> val = value.entrySet();val.forEach( v -> {System.out.println("成绩是否合格:" + v.getKey());System.out.println("结果:" + v.getValue());});});System.out.println("***************版本分界线**************************");// 1.8写法Map>> collect = students.stream().collect(Collectors.partitioningBy(Student::getAtSchool, Collectors.partitioningBy(s -> {if (s.getScore() > 60) {return true;}return false;})));Set>>> entries = collect.entrySet();/*key2:false成绩是否合格:false结果:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}]成绩是否合格:true结果:[Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false},Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]----------------------------------------key:true成绩是否合格:false结果:[Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true},Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]成绩是否合格:true结果:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}]*/entries.forEach(entrie -> {System.out.println("----------------------------------------");System.out.println("key:" + entrie.getKey());Map> value = entrie.getValue();Set>> val = value.entrySet();val.forEach( v -> {System.out.println("成绩是否合格" + v.getKey());System.out.println("结果" + v.getValue());});});
}