在java stream中,我们通常需要将处理后的stream转换成集合类,这个时候就需要用到stream.collect方法。collect方法需要传入一个Collector类型,要实现Collector还是很麻烦的,需要实现好几个接口。
于是java提供了更简单的Collectors工具类来方便我们构建Collector。
Collectors
是java.util.stream
包下的一个工具类,其中各个方法的返回值可以作为java.util.stream.Stream#collect
的入参,实现对队列的各种操作,包括:分组、聚合等。
这几个函数比较简单,是将聚合之后的元素,重新封装到队列中,然后返回。比如,得到所有Student
的 ID 列表,只需要根据需要的结果类型使用不同的方法即可:
// List: [1, 2, 3]
final List
idList = students.stream().map(Student::getId).collect(Collectors.toList()); // Set: [1, 2, 3]
final Set
idSet = students.stream().map(Student::getId).collect(Collectors.toSet()); // TreeSet: [1, 2, 3]
final Collection
idTreeSet = students.stream().map(Student::getId).collect(Collectors.toCollection(TreeSet::new));
注意:
toList
方法返回的是List
子类,toSet
返回的是Set
子类,toCollection
返回的是Collection
子类。我们都知道,Collection
的子类包括List
、Set
等众多子类,所以toCollection
更加灵活。
这两个方法的作用是将聚合元素,重新组装为Map
结构,也就是 k-v 结构。两者用法一样,区别是toMap
返回的是Map
,toConcurrentMap
返回ConcurrentMap
,也就是说,toConcurrentMap
返回的是线程安全的 Map 结构。
比如,我们需要聚合Student
的 id:
// {1=Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123), 2=Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), 3=Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)}
final Map
map11 = students.stream() .collect(Collectors.toMap(Student::getId, Function.identity()));
但是,如果 id 有重复的,会抛出java.lang.IllegalStateException: Duplicate key
异常,所以,为了保险起见,我们需要借助toMap
另一个重载方法:
// {1=Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123), 2=Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), 3=Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123)}
final Map
map2 = students.stream() .collect(Collectors.toMap(Student::getId, Function.identity(), (x, y) -> x));
可以看到,toMap
有不同的重载方法,可以实现比较复杂的逻辑。比如,我们需要得到根据 id 分组的Student
的姓名:
// {1=张三, 2=李四, 3=王五}
final Map
map3 = students.stream() .collect(Collectors.toMap(Student::getId, Student::getName, (x, y) -> x));
比如,我们需要得到相同年龄得分最高的Student
对象集合:
// {10=Student(id=3, name=王五, birthday=2011-03-03, age=10, score=32.123), 11=Student(id=2, name=李四, birthday=2010-02-02, age=11, score=22.123), 12=Student(id=1, name=张三, birthday=2009-01-01, age=12, score=12.123)}
final Map
map5 = students.stream() .collect(Collectors.toMap(Student::getAge, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(Student::getScore))));
所以,toMap
可玩性很高。
groupingBy
与toMap
都是将聚合元素进行分组,区别是,toMap
结果是 1:1 的 k-v 结构,groupingBy
的结果是 1:n 的 k-v 结构。
mapping
是先通过Function
函数处理数据,然后通过Collector
方法聚合元素。比如,获取获取students
的姓名列表:
// [张三, 李四, 王五]
students.stream()
.collect(Collectors.mapping(Student::getName, Collectors.toList()));
这种计算与java.util.stream.Stream#map
方式类似:
// [张三, 李四, 王五]
students.stream()
.map(Student::getName)
.collect(Collectors.toList());
从这点上看,还是通过java.util.stream.Stream#map
更清晰一些。
reducing
提供了 3 个重载方法:
public static
:直接通过BinaryOperator
操作,返回值是Optional
public static
:预定默认值,然后通过BinaryOperator
操作
public static
:预定默认值,通过Function
操作元素,然后通过BinaryOperator
操作
比如,计算所有students
的得分总数:
// Optional[66.369],注意返回类型是Optional
students.stream()
.map(Student::getScore)
.collect(Collectors.reducing(Double::sum));
// 66.369
students.stream()
.map(Student::getScore)
.collect(Collectors.reducing(0.0, Double::sum));
// 66.369
students.stream()
.collect(Collectors.reducing(0.0, Student::getScore, Double::sum));
同mapping
,reducing
的操作与java.util.stream.Stream#reduce
方式类似:
一文掌握 Java8 Stream 中 Collectors 的 24 个操作_Java_看山_InfoQ写作社区