Java 8 引入的 Stream
极大地简化了集合数据的处理,提供了一种现代、函数式的方式来处理数据。然而,在处理流时,我们经常需要将流的结果汇总到集合中或者进行各种统计计算。这就是收集器(Collectors)发挥作用的地方。本文将深入探讨 Java 8 Stream 中的收集器,介绍收集器的各种用法和技巧,帮助你更好地利用收集器处理数据。
什么是收集器(Collectors)
收集器是 Stream
提供的一个重要功能,用于将流的元素收集到一个结果容器中。通过使用收集器,可以让代码更加方便的进行简化与重用。其内部主要核心是通过 Collectos
完成更加复杂的计算转换从而获取到最终结果。
Java 8 在 Collectors
类中预定义了多个用于收集的方法,使得我们可以轻松地对流的元素进行汇总、分组、分区以及其他各种操作。
常见的收集器用法
通过 toList 将元素收集到集合中
List names = peopleStream.map(Person::getName)
.collect(Collectors.toList());
通过 counting 统计集合总数
Long collect = studentList.stream().collect(Collectors.counting());
通过 maxBy 和 minBy 获取最大值最小值
Optional optional = studentList.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge));)
if (optional.isPresent()){
Student student = optional.get();
System.out.println(student);
}
通过 summingLnt 进行数据汇总
Integer collect = peopleStream.collect(Collectors.summingLnt(Person::getAge));
int sum = peopleStream.mapToInt(Person::getAge).sum();
通过 averagingLnt 进行平均值获取
Double collect = peopleStream.collect(Collectors.averagingLnt(Person::getAge));
其内部是在收集过程中,对所有年龄进行累加,最后除以平
均值
通过 joining 进行数据拼接
Person collect = peopleStream.collect(Collectors.joining());
这种方式相当于将流中每一个元素的name属性获取映射,内部通过StringBuilder来把每一个映射的值进行拼接。
通过 groupingBy 将数据进行分组
Map map = peopleStream.collect(Collectors.groupingBy(Person::getAge));
自定义收集器
已经通过Collectors中提供的静态方法,完成了诸多的收集器操作,虽然它本身提供了诸多方法,但是不一定能够覆盖日常开发中的所有需求,因此,有时还需要我们根据自身的业务去自定义收集器的实现。通过实现 Collector
接口,我们可以完全掌控收集的过程。
源码分析
根据源码,Collector 接口需要三个参数。
- T: 流中要收集的元素类型
- A: 累加器的类型
- R:收集的结果类型
如果想要自定义收集器,需要实现 Collector 接口中的五个方法 Supplier、Accumulator、finisher、combiner、characteristics
-
supplier:用于创建一个容器,在调用它时,需要创建一个空的累加器实例,供后续方法使用。
-
accumulator:基于supplier中创建的累加容器,进行累加操作。
-
finisher:当遍历完流后,在其内部完成最终转换,返回一个最终结果。
-
combiner:用于在并发情况下,将每个线程的容器进行合并。
-
characteristics:用于定义收集器行为,如是否可以并行或使用哪些优化。其本身是一个枚举,内部有三个值,分别为:
- CONCURRENT:表明收集器是并行的且只会存在一个中间容器。
- UNORDERED:表明结果不受流中顺序影响,收集是无序的。
- IDENTITY_FINISH:表明累积器的结果将会直接作为归约的最终结果,跳过finisher()。
实战示例:收集合格的学生
- 定义自定义收集器
package com.jdk8.features.stream.collector;
import com.jdk8.features.lambda.Student;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
public class MyCollector implements Collector {
@Override
public Supplier supplier() {
return ArrayList::new;
}
@Override
public BiConsumer accumulator() {
return ((studentList, student) -> {
if (student.getIsPass()){
studentList.add(student);
}
});
}
@Override
public BinaryOperator combiner() {
return null;
}
@Override
public Function finisher() {
return Function.identity();
}
@Override
public Set characteristics() {
return EnumSet.of(Characteristics.IDENTITY_FINISH,Characteristics.UNORDERED);
}
}
- 使用自定义收集器
import com.jdk8.features.lambda.Student;
import java.util.ArrayList;
import java.util.List;
public class MyCollectorDemo {
public static void main(String[] args) {
List studentList = new ArrayList();
studentList.add(new Student(1,"张三",18,19));
studentList.add(new Student(2,"李四",19,18));
studentList.add(new Student(3,"王五",20,21));
studentList.add(new Student(4,"赵六",21,80));
List list = studentList.stream().collect(new MyCollector());
System.out.println(list);
}
}
结语
掌握收集器的使用是精通 Java 8 Stream 不可或缺的一部分。在处理流数据时,收集器可以帮助我们更轻松地完成数据的汇总、分组、分区等操作。通过本文的介绍,相信你已经对收集器有了更深入的了解,并能够在实际项目中灵活运用收集器的各种技巧。让我们在流处理的旅程中更加游刃有余!