前言

以前写过Java8中的自定义收集器,当时只是在文章末尾放了个例子,觉得基本用法挺简单,而且有些东西没搞懂(比如combiner方法到底做什么的),没有专门写,过了一段时间又忘了,所以,即使还是没搞懂combiner方法,还是硬着头皮把使用的经验记录下,方便以后参考。

简介

要实现自定义收集器,只需要实现java.util.stream.Collector<T, A, R>接口即可,这个接口包含五个无参方法:[supplier, accumulator, combiner, finisher, characteristics]。

泛型简介

泛型含义如下:

  • T:缩减操作的输入元素的类型
  • A:还原操作的可变累积类型(通常隐藏为实现细节)
  • R:还原操作的结果类型

T不必说,收集什么泛型的列表,就输入什么类型,比如我对一个Student列表进行收集计算,那么T肯定是Student。

A是计算过程中用来盛放计算结果的容器,一般都是List,Set等等。

R就比较好理解,就是收集完成后返回的类型,需要注意的是,当characteristics()中包含Characteristics.IDENTITY_FINISH时,。

方法简介

  • characteristics 表示收集计算的方式,返回类型为Set<Characteristics>,其中Characteristics是一个枚举类型,指示收集器属性的特征,可用于优化缩减实现。它只有三个值[CONCURRENT, UNORDERED, IDENTITY_FINISH],注释翻译过来分别是:1.表示此收集器是并发的,这意味着结果容器可以支持与来自多个线程的相同结果容器同时调用的累加器函数。如果CONCURRENT收集器也不是UNORDERED,那么只有在应用于无序数据源时才应同时评估它。2.指示集合操作不承诺保留输入元素的遭遇顺序。(如果结果容器没有内在顺序,例如Set,则可能是这样。)3.表示整理器功能是标识功能,可以省略。 如果设置,则必须是从A到R的未经检查的强制转换成功的情况。
  • supplier 该方法返回一个Supplier<A>类型的结果,表示在计算过程中,如何初始化一个临时容器,比如A=List,那么一般返回ArrayList::new
  • accumulator 核心方法,关键的计算逻辑都放在这里,定义了如何把一个个元素放入临时容器中,返回类型为BiConsumer<A, T>
  • combiner 返回一个BinaryOperator<A>类型的结果,个人理解是如何合并临时容器,但是在实际应用中没碰到执行过,所以一般直接返回null
  • finisher 定义了如何把临时容器转换为输出结果,返回类型为Function<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>

需求1

有一个学生列表,求计算学生的总分

数据结构

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);

结果

需求2

将输出结果转化成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()方法即可,如下所示:

        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>>是一样的,但看起来更简洁明了!

Java8学习笔记(十)--自定义收集器的更多相关文章

  1. Struts2学习笔记(十)——自定义拦截器

    Struts2拦截器是基于AOP思想实现的,而AOP的实现是基于动态代理.Struts2拦截器会在访问某个Action之前或者之后进行拦截,并且Struts2拦截器是可插拔的:Struts2拦截器栈就 ...

  2. SpringBoot学习笔记:自定义拦截器

    SpringBoot学习笔记:自定义拦截器 快速开始 拦截器类似于过滤器,但是拦截器提供更精细的的控制能力,它可以在一个请求过程中的两个节点进行拦截: 在请求发送到Controller之前 在响应发送 ...

  3. java8学习之自定义收集器深度剖析与并行流陷阱

    自定义收集器深度剖析: 在上次[http://www.cnblogs.com/webor2006/p/8342427.html]中咱们自定义了一个收集器,这对如何使用收集器Collector是极有帮助 ...

  4. java8学习之自定义收集器实现

    在上次花了几个篇幅对Collector收集器的javadoc进行了详细的解读,其涉及到的文章有: http://www.cnblogs.com/webor2006/p/8311074.html htt ...

  5. Java8学习笔记目录

    Java8学习笔记(一)--Lambda表达式 Java8学习笔记(二)--三个预定义函数接口 Java8学习笔记(三)--方法引入 Java8学习笔记(四)--接口增强 Java8学习笔记(五)-- ...

  6. [转载]SharePoint 2013搜索学习笔记之自定义结果源

    搜索中心新建好之后在搜索结果页上会默认有所有内容,人员,对话,视频这四个结果分类,每个分类会返回指定范围的搜索结果,这里我再添加了部门日志结果分类,搜索这个分类只会返回部门日志内容类型的搜索结果,要实 ...

  7. Java8学习笔记----Lambda表达式 (转)

    Java8学习笔记----Lambda表达式 天锦 2014-03-24 16:43:30 发表于:ATA之家       本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人 ...

  8. python3.4学习笔记(十八) pycharm 安装使用、注册码、显示行号和字体大小等常用设置

    python3.4学习笔记(十八) pycharm 安装使用.注册码.显示行号和字体大小等常用设置Download JetBrains Python IDE :: PyCharmhttp://www. ...

  9. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

随机推荐

  1. 提升PHP安全:8个必须修改的PHP默认配置

    很明显,PHP+Mysql+Apache是很流行的web技术,这个组合功能强大,可扩展性强,还是免费的.然而,PHP的默认设置对已经上线的网站不是那么适合.下面通过修改默认的配置文件加强PHP的安全策 ...

  2. node+koa2 postman 测试post请求

    注意:一定要写成  “” ,否则后台一直接收不到 router.post('/form',async(ctx,next)=>{ const form1 = ctx.request.body; c ...

  3. Centos 7 安装 Mysql 5.5 5.6 5.7

    环境 [root@node1 ~]# cat /etc/redhat-release CentOS Linux release (Core) [root@node1 ~]# uname -a Linu ...

  4. RxJava2学习笔记(2)

    上一篇已经熟悉了Observable的基本用法,但是如果仅仅只是“生产-消费”的模型,这就体现不出优势了,java有100种办法可以玩这个:) 一.更简单的多线程 正常情况下,生产者与消费者都在同一个 ...

  5. C#线程篇---Task(任务)和线程池不得不说的秘密

    我们要知道的是,QueueUserWorkItem这个技术存在许多限制.其中最大的问题是没有一个内建的机制让你知道操作在什么时候完成,也没有一个机制在操作完成是获得一个返回值,这些问题使得我们都不敢启 ...

  6. IPOPT工具解决非线性规划最优化问题使用案例

    IPOPT工具解决非线性规划最优化问题使用案例 By Andrew( justastriver@gmail.com ) 2013-08-07 简单介绍 ipopt是一个解决非线性规划最优化问题的工具集 ...

  7. Knockout.Js官网学习(selectedOptions绑定、uniqueName 绑定)

    selectedOptions绑定 selectedOptions绑定用于控制multi-select列表已经被选择的元素,用在使用options绑定的<select>元素上. 当用户在m ...

  8. Miscellaneos:ISV

    ylbtech-Miscellaneos:ISV ISV英文全称是Independent Software Vendors ,意为“独立软件开发商”,特指专门从事软件的开发.生产.销售和服务的企业,如 ...

  9. 浅谈压缩感知(二十三):压缩感知重构算法之压缩采样匹配追踪(CoSaMP)

    主要内容: CoSaMP的算法流程 CoSaMP的MATLAB实现 一维信号的实验与结果 测量数M与重构成功概率关系的实验与结果 一.CoSaMP的算法流程 压缩采样匹配追踪(CompressiveS ...

  10. 【asp.net core】Publish to a Linux-Ubuntu 14.04 Server Production Environment

    Submary 又升级了,目录结构有变化了 . project.json and Visual Studio 2015 with .NET Core On March 7, 2017, the .NE ...