之前写过Java8中的自定义收集器,当时只是在文章末尾放了个例子,以为基本用法挺简单,并且有些东西没搞懂(好比combiner方法到底作什么的),没有专门写,过了一段时间又忘了,因此,即便仍是没搞懂combiner方法,仍是硬着头皮把使用的经验记录下,方便之后参考。java
要实现自定义收集器,只须要实现java.util.stream.Collector<T, A, R>
接口便可,这个接口包含五个无参方法:[supplier, accumulator, combiner, finisher, characteristics]。json
泛型含义以下:数据结构
T
没必要说,收集什么泛型的列表,就输入什么类型,好比我对一个Student列表进行收集计算,那么T确定是Student。
A
是计算过程当中用来盛放计算结果的容器,通常都是List,Set等等。
R
就比较好理解,就是收集完成后返回的类型,须要注意的是,当characteristics()中包含Characteristics.IDENTITY_FINISH时,。并发
Set<Characteristics>
,其中Characteristics
是一个枚举类型,指示收集器属性的特征,可用于优化缩减实现。它只有三个值[CONCURRENT, UNORDERED, IDENTITY_FINISH]
,注释翻译过来分别是:1.表示此收集器是并发的,这意味着结果容器能够支持与来自多个线程的相同结果容器同时调用的累加器函数。若是CONCURRENT收集器也不是UNORDERED,那么只有在应用于无序数据源时才应同时评估它。2.指示集合操做不承诺保留输入元素的遭遇顺序。(若是结果容器没有内在顺序,例如Set,则多是这样。)3.表示整理器功能是标识功能,能够省略。 若是设置,则必须是从A到R的未经检查的强制转换成功的状况。Supplier<A>
类型的结果,表示在计算过程当中,如何初始化一个临时容器,好比A=List,那么通常返回ArrayList::newBiConsumer<A, T>
BinaryOperator<A>
类型的结果,我的理解是如何合并临时容器,可是在实际应用中没碰到执行过,因此通常直接返回nullFunction<A, R>
,须要注意的是,当方法characteristics
的返回值中包含Characteristics.IDENTITY_FINISH
,则必须保证从A到R可以强制转换成功,此时该方法固定返回Function.identity()
便可。不然会报错。好比A为ArrayList,而R为HashMap的状况就会报错。<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.33</version> </dependency>
有一个学生列表,求计算学生的总分ide
public class Student { private String id; private Course course; private double score; public Student() { } public Student(String id, Course course, double score) { this.id = id; this.course = course; this.score = score; } @Override public String toString() { return JSON.toJSONString(this, true); } public enum Course { LANGUAGE, MATHEMATICS, ENGLISH, TOTAL } // 省略setter、getter方法 }
public static final List<Student> students = List.of(new Student("1", Student.Course.ENGLISH, 78), new Student("1", Student.Course.LANGUAGE, 71), new Student("1", Student.Course.MATHEMATICS, 82), new Student("2", Student.Course.ENGLISH, 69), new Student("2", Student.Course.LANGUAGE, 66), new Student("2", Student.Course.MATHEMATICS, 46), new Student("3", Student.Course.ENGLISH, 78), new Student("3", Student.Course.LANGUAGE, 88), new Student("3", Student.Course.MATHEMATICS, 100), new Student("4", Student.Course.ENGLISH, 68), new Student("4", Student.Course.LANGUAGE, 84), new Student("4", Student.Course.MATHEMATICS, 90), new Student("5", Student.Course.ENGLISH, 74), new Student("5", Student.Course.LANGUAGE, 59), new Student("5", Student.Course.MATHEMATICS, 87));
public class StudentCollector implements Collector<Student, List<Student>, List<Student>> { @Override public Supplier<List<Student>> supplier() { return ArrayList::new; } @Override public BiConsumer<Set<Student>, Student> accumulator() { return (set, student) -> { Predicate<Student> studentPredicate = s -> s.getId().equals(student.getId()); boolean b = set.stream().noneMatch(studentPredicate); if (b) { student.setCourse(Student.Course.TOTAL); set.add(student); } set.stream().filter(studentPredicate).forEach(s -> s.setScore(s.getScore() + student.getScore())); }; } @Override public BinaryOperator<List<Student>> combiner() { return null; } @Override public Function<List<Student>, List<Student>> finisher() { return Function.identity(); } @Override public Set<Characteristics> characteristics() { return EnumSet.of(Characteristics.IDENTITY_FINISH); } }
List<Student> studentList = students.stream().collect(new StudentCollector()); System.out.println(studentList);
将输出结果转化成TreeMap<Double, Student>
,key为学生总分,value为学生自己,并按照总分从高到低排序。函数
数据结构与学生列表不变。优化
public class StudentDiffCollectors implements Collector<Student, Set<Student>, TreeMap<Double, Student>> { @Override public Supplier<Set<Student>> supplier() { return HashSet::new; } @Override public BiConsumer<Set<Student>, Student> accumulator() { return (set, student) -> { Predicate<Student> studentPredicate = s -> s.getId().equals(student.getId()); boolean b = set.stream().noneMatch(studentPredicate); if (b) { student.setCourse(Student.Course.TOTAL); set.add(student); } set.stream().filter(studentPredicate).forEach(s -> s.setScore(s.getScore() + student.getScore())); }; } @Override public BinaryOperator<Set<Student>> combiner() { return null; } @Override public Function<Set<Student>, TreeMap<Double, Student>> finisher() { return list -> { TreeMap<Double, Student> doubleStudentTreeMap = new TreeMap<>(Comparator.reverseOrder()); list.forEach(student -> doubleStudentTreeMap.put(student.getScore(), student)); return doubleStudentTreeMap; }; } @Override public Set<Characteristics> characteristics() { return EnumSet.of(Characteristics.CONCURRENT, Characteristics.UNORDERED); } }
TreeMap<Double, Student> collect = students.stream().collect(new StudentDiffCollectors()); System.out.println(collect);
除了实现接口之外,jdk还帮咱们提供了更简单的实现,只须要调用Collector.of()
方法便可,以下所示:this
Collector<Student, List<Student>, List<Student>> studentCollector = Collector.of(ArrayList::new, (l, r) -> { // 计算逻辑 }, (l, r) -> null, Function.identity(), Collector.Characteristics.IDENTITY_FINISH);
因为它符合A强转为R的条件,所以,能够更简化为:线程
Collector<Student, List<Student>, List<Student>> studentCollector = Collector.of(ArrayList::new, (l, r) -> { // 计算逻辑 }, (l, r) -> null);
它的效果和实现接口Collector<Student, List<Student>, List<Student>>
是同样的,但看起来更简洁明了!翻译