收集器Collectors是对Stream API的一个补充,就好像我们的集合类有一个Collections工具类一样,Collectors内部实现了大量的静态方法,方便我们进行流的规约、求和、求最、分组等等操作。
本节主要讲解的内容:
收集数据
汇总和规约
求值工厂
字符串拼接
分组
分区
我们来看代码,先构建测试数据:
List<Student> list = new ArrayList<>();
String[] names = {"小明","小华","小志","小东","小李","小张","小王","小周","小吴","小郑"};
for(int i=0; i<10; i++) {
Student student = Student.builder()
.name(names[i])
.age(12+i/5)
.classId(i%3+1)
.no(i+1)
.math(85d+i).build();
list.add(student);
}
//初始化数据
System.out.println("======初始化数据:======");
list.forEach(System.out::println);
输出结果:
======初始化数据:======
Student(classId=1, no=1, name=小明, age=12, math=85.0, chinese=null, english=null, score=null)
Student(classId=2, no=2, name=小华, age=12, math=86.0, chinese=null, english=null, score=null)
Student(classId=3, no=3, name=小志, age=12, math=87.0, chinese=null, english=null, score=null)
Student(classId=1, no=4, name=小东, age=12, math=88.0, chinese=null, english=null, score=null)
Student(classId=2, no=5, name=小李, age=12, math=89.0, chinese=null, english=null, score=null)
Student(classId=3, no=6, name=小张, age=13, math=90.0, chinese=null, english=null, score=null)
Student(classId=1, no=7, name=小王, age=13, math=91.0, chinese=null, english=null, score=null)
Student(classId=2, no=8, name=小周, age=13, math=92.0, chinese=null, english=null, score=null)
Student(classId=3, no=9, name=小吴, age=13, math=93.0, chinese=null, english=null, score=null)
Student(classId=1, no=10, name=小郑, age=13, math=94.0, chinese=null, english=null, score=null)
1、收集数据,例如toList()方法,我们之前已经用了很多次,作用就是将输入元素累加到一个新的List中并返回,这个累加并不是数值相加,而是积累的意思,相当于就是list.add(x)
//收集数据
List<Double> toList = list.stream().map(Student::getMath).collect(Collectors.toList());
System.out.println("收集学生数学成绩存入List中:"+toList);
输出结果:
收集学生数学成绩存入List中:[85.0, 86.0, 87.0, 88.0, 89.0, 90.0, 91.0, 92.0, 93.0, 94.0]
2、汇总和规约,收集器中的汇总规约和Stream中的汇总规约作用是一样的,我们来看4个例子
Long counting = list.stream().collect(Collectors.counting());
System.out.println("统计数据总数:"+counting);
long count = list.stream().count();
System.out.println("统计数据总数:"+count);
Optional<Student> max = list.stream().max(Comparator.comparing(Student::getMath));
System.out.println("数学最高分学生:"+max.get());
Optional<Student> collect = list.stream().collect(maxBy(Comparator.comparing(Student::getMath)));
System.out.println("数学最高分学生:"+collect.get());
Double summingDouble = list.stream().collect(Collectors.summingDouble(Student::getMath));
System.out.println("所有学生数学分数加和:"+summingDouble);
Double averagingDouble = list.stream().collect(Collectors.averagingDouble(Student::getMath));
System.out.println("班级学生数学平均分:"+averagingDouble);
输出结果:
统计数据总数:10
统计数据总数:10
数学最高分学生:Student(classId=1, no=10, name=小郑, age=13, math=94.0, chinese=null, english=null, score=null)
数学最高分学生:Student(classId=1, no=10, name=小郑, age=13, math=94.0, chinese=null, english=null, score=null)
所有学生数学分数加和:895.0
班级学生数学平均分:89.5
3、求值工厂,通过求值工厂方法,能够帮我们一次性计算出:数量、求和、最小值、平均值、最大值
DoubleSummaryStatistics summarizingDouble = list.stream().collect(Collectors.summarizingDouble(Student::getMath));
System.out.println("求值工厂自动计算数量、求和、最小值、平均值、最大值:"+summarizingDouble);
输出结果:
求值工厂自动计算数量、求和、最小值、平均值、最大值:DoubleSummaryStatistics{
count=10,
sum=895.000000,
min=85.000000,
average=89.500000,
max=94.000000
}
4、字符串值拼接,joining方法支持按照指定分隔符进行字符串拼接,其内部使用了StringJoiner来进行字符串拼接,StringJoiner是jdk提供的高效字符串拼接类
//joining-拼接
String joining = list.stream().map(Student::getName).collect(Collectors.joining(","));
System.out.println("拼接学生姓名:"+joining);
输出结果:
拼接学生姓名:小明,小华,小志,小东,小李,小张,小王,小周,小吴,小郑
5、分组,和数据库中的group by是相似的功能,这个功能是Stream中没有直接提供的功能,所以收集器类为我们提供了更丰富、更方便的API
//分组
Map<Integer, List<Student>> groupBy = list.stream().collect(Collectors.groupingBy(Student::getClassId));
groupBy.forEach((x,y)->{
System.out.println("班级:"+x+"--------------");
y.forEach(System.out::println);
});
//分组计数
Map<Integer, Long> collect1 = list.stream().collect(Collectors.groupingBy(Student::getClassId, Collectors.counting()));
System.out.println("按照班级统计各班人数:"+ collect1);
//按班级ID分组计算数学最高分学生
Map<Integer, Optional<Student>> collect2 = list.stream().collect(Collectors.groupingBy(Student::getClassId, Collectors.maxBy(Comparator.comparingDouble(Student::getMath))));
collect2.forEach((x,y)->{
System.out.println("班级ID:"+x.intValue()+",最高分学生:"+y.get().getName()+",数学最高分:"+y.get().getMath());
});
输出结果:
班级:1--------------
Student(classId=1, no=1, name=小明, age=12, math=85.0, chinese=null, english=null, score=null)
Student(classId=1, no=4, name=小东, age=12, math=88.0, chinese=null, english=null, score=null)
Student(classId=1, no=7, name=小王, age=13, math=91.0, chinese=null, english=null, score=null)
Student(classId=1, no=10, name=小郑, age=13, math=94.0, chinese=null, english=null, score=null)
班级:2--------------
Student(classId=2, no=2, name=小华, age=12, math=86.0, chinese=null, english=null, score=null)
Student(classId=2, no=5, name=小李, age=12, math=89.0, chinese=null, english=null, score=null)
Student(classId=2, no=8, name=小周, age=13, math=92.0, chinese=null, english=null, score=null)
班级:3--------------
Student(classId=3, no=3, name=小志, age=12, math=87.0, chinese=null, english=null, score=null)
Student(classId=3, no=6, name=小张, age=13, math=90.0, chinese=null, english=null, score=null)
Student(classId=3, no=9, name=小吴, age=13, math=93.0, chinese=null, english=null, score=null)
按照班级统计各班人数:{1=4, 2=3, 3=3}
班级ID:1,最高分学生:小郑,数学最高分:94.0
班级ID:2,最高分学生:小周,数学最高分:92.0
班级ID:3,最高分学生:小吴,数学最高分:93.0
6、分区,是特殊的分组,分组只能分为两组,即true和false
//分区,数学成绩大于等于90则为优秀
Map<Boolean, List<Student>> partitioningBy = list.stream().collect(partitioningBy(x -> x.getMath() >= 90));
String collect3 = partitioningBy.get(true).stream().map(Student::getName).collect(Collectors.joining(","));
System.out.println("数学成绩优秀的学生:"+collect3);
String collect4 = partitioningBy.get(false).stream().map(Student::getName).collect(Collectors.joining(","));
System.out.println("数学成绩非优秀的学生:"+collect4);
输出结果:
数学成绩优秀的学生:小张,小王,小周,小吴,小郑
数学成绩非优秀的学生:小明,小华,小志,小东,小李