Java Stream 自定义Collector
Collector的使用
使用Java Stream流操作数据时,经常会用到各种Collector收集器来进行数据收集。
这里便深入了解一点去了解Collector的工作原理和如何自定义Collector。
使用例子为:
// String joining
String foodNameList1 = foodList.stream().map(Food::getSimpleName).collect(Collectors.joining(","));
String foodNameList2 = foodList.stream().map(Food::getSimpleName).reduce("", (a, b) -> String.join(",", a, b));
String foodNameList3 = foodList.stream().collect(Collectors.reducing("", Food::getSimpleName, (a, b) -> String.join(",", a, b)));
// group by operation
Map<String, Map<String, List<Food>>> cookingAndCategoryMap = foodList.stream().collect(Collectors.groupingBy(Food::getCookingStyle, HashMap::new, Collectors.groupingBy(Food::getCategory)));
Map<String, Food> cookingAndPriceMap = foodList.stream().collect(Collectors.groupingBy(Food::getCookingStyle, HashMap::new, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Food::getPrice)), Optional::get)));
Collector<T, A, R> 接口
Collector Interface 包含一系列方法,为实现具体的规约操作提供了范本。
我们可以通过实现Collector接口来自定义自己的收集器,从而可以自由地创建自定义规约操作。
要想自定义收集器,必然需要先理解Collector接口的定义。
其中接口泛型类定义如下:
-T是流中要收集的项目的泛型 。
-A是累加器的泛型,累加器在收集过程中用于累积部分结果。
-R是收集操作得到的对象的类型(通常是集合)。
Collector Interface 定义如下:
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
/**
* Characteristics indicating properties of a {@code Collector}, which can
* be used to optimize reduction implementations.
*/
enum Characteristics {
CONCURRENT,
UNORDERED,
IDENTITY_FINISH
}
}
Collector 接口方法
建立新的结果容器:supplier方法
supplier方法需返回一个Supplier,也就是一个无参数函数。
在调用时会创建一个空的累加器实例,供数据收集过程使用。
如Collectors.toList()中supplier实现为:
return ArrayList:new;将元素添加到结果容器:accumulator方法
accumulator方法会返回执行规约操作的函数,每次执行函数都会更新累加器。
BiConsumer 无返回值,原位更新累加器。两个参数分别为保存规约结果的累加器和遍历元素。
如Collectors.toList()中accumulator方法实现为:
return List:add;对结果容器应用最终转换:finisher方法
这是在遍历完流之后,在累积过程的最后要调用的一个函数,以便将累加器对象转换为整个集合操作的最终结果。
通常,累加器对象便是最终结果。如Collectors.toList()方法中finisher实现为:
return List:addAll;合并两个结果容器:combiner方法
返回一个供规约操作使用的函数,定义了对流的各个子部分进行并行处理时,各个子部分要如何合并。
即将多个累加器合并为一个,如Collectors.toList()中combiner实现为:
return List:addALL定义收集器的行为:characteristics方法
返回一份不可变的Characteristic集合,它定义了收集器的行为——关于流是否可以进行并行规约、可以使用那些优化的提示。总分包含三个部分:- UNORDERED——规约结果不受流中项目的遍历和累积顺序的影响
- CONCURRENT——accumulator函数可以从多个线程同时调用,且该收集器可以并行规约流。
- IDENTITY_FINSIH——表示完成器方法返回的函数是一个恒等函数,可以跳过。这种情况下,
累加器对象将会直接用作规约过程的最终结果。即不会将累加器A转化为结果R。
至此,Collector 接口定义的方法便全部了解了,使用前三个方法便能完成顺序流的规约,规约过程如下:

在前三个方法的基础上,再加上第四个方法便能支持并行流的规约,过程如下:

实现自定义Collector
了解完成Collector相关的接口方法定义和规约过程之后,我们便可以开始自定义Collector 实现了。
创建一个将String 元素放入LinkedList 的收集器,如下:
public class MyCollector implements Collector<String, List, List>{
@Override
public Supplier<List> supplier() {
return LinkedList::new;
}
@Override
public BiConsumer<List, String> accumulator() {
return List::add;
}
@Override
public BinaryOperator<List> combiner() {
return (r1, r2) -> {
r1.addAll(r2);
return r1;
};
}
@Override
public Function<List, List> finisher() {
return list -> list;
}
@Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Characteristics.IDENTITY_FINISH);
}
}
List<String> simpleNameList = foodList.stream().map(Food::getSimpleName).collect(new MyCollector());
Java Stream 自定义Collector的更多相关文章
- java stream collector
Java Stream API进阶篇 本文github地址 上一节介绍了部分Stream常见接口方法,理解起来并不困难,但Stream的用法不止于此,本节我们将仍然以Stream为例,介绍流的规约操作 ...
- java stream中Collectors的用法
目录 简介 Collectors.toList() Collectors.toSet() Collectors.toCollection() Collectors.toMap() Collectors ...
- 深度掌握 Java Stream 流操作,让你的代码高出一个逼格!
概念 Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选.排序.聚合等. Stream 的操作符大体上分为两种:中间操作符和终止操作符 中 ...
- Java Stream 使用详解
Stream是 Java 8新增加的类,用来补充集合类. Stream代表数据流,流中的数据元素的数量可能是有限的,也可能是无限的. Stream和其它集合类的区别在于:其它集合类主要关注与有限数量的 ...
- Java Stream函数式编程图文详解(二):管道数据处理
一.Java Stream管道数据处理操作 在本号之前发布的文章<Java Stream函数式编程?用过都说好,案例图文详解送给你>中,笔者对Java Stream的介绍以及简单的使用方法 ...
- [源码解析] 当 Java Stream 遇见 Flink
[源码解析] 当 Java Stream 遇见 Flink 目录 [源码解析] 当 Java Stream 遇见 Flink 0x00 摘要 0x01 领域 1.1 Flink 1.2 Java St ...
- Java Stream 源码分析
前言 Java 8 的 Stream 使得代码更加简洁易懂,本篇文章深入分析 Java Stream 的工作原理,并探讨 Steam 的性能问题. Java 8 集合中的 Stream 相当于高级版的 ...
- Java Stream API性能测试
已经对Stream API的用法鼓吹够多了,用起简洁直观,但性能到底怎么样呢?会不会有很高的性能损失?本节我们对Stream API的性能一探究竟. 为保证测试结果真实可信,我们将JVM运行在-ser ...
- java stream 原理
java stream 原理 需求 从"Apple" "Bug" "ABC" "Dog"中选出以A开头的名字,然后从中选 ...
随机推荐
- 阿里云中quick bi用地图分析数据时维度需转换为地理区域类型
1.到数据集里面点击编辑要做地图分析的数据集 2.找到要分析的地理维度字段,选择转换为对应的类型,这里为市级,所以选择转换为市,其它类似,然后点击右上角保存即可. 3.返回数据集,点击新建仪表板 4. ...
- 44、wget和curl的常用参数
1.wget: wget是文件下载的工具: 不加任何参数是直接下载该文件: (1)-O: 将下载的文件指定为特定的文件名: wget -O baidu.html www.baidu.com --201 ...
- 个人使用uploadify插件遇到的一些问题
当uploadify上传插件遇到的好几个问题 现在开始自我反省,留下脚印希望能够帮助其他遇到同样问题的朋友. 我遇到的第一个是, 在firefox不能执行uploadify事件onUploadSucc ...
- SpringBoot:SpringCloud与SpringBoot兼容版本参(其它组件兼容情况)
SpringCloud --- Springboot 版本兼容 SpringCloud SpringBoot Edgware.SR5 >=1.5.0.RELEASE and <=1.5.2 ...
- 2012年第三届蓝桥杯C/C++程序设计本科B组省赛 方阵旋转(代码填空)
方阵旋转 对一个方阵转置,就是把原来的行号变列号,原来的列号变行号 例如,如下的方阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 转置后变为: 1 5 9 13 2 ...
- get和post两种表单提交方式的区别
今天看到一篇博客谈论get和post区别,简单总结一下https://www.cnblogs.com/logsharing/p/8448446.html 要说两者的区别,接触过web开发的人基本上都能 ...
- B 站崩了,受害程序员聊聊
非吃瓜,B 站事件始末分析 + 防治技术分享 大家好,我是鱼皮,昨天小破站崩了的事情相信很多朋友都听说了. 这要是搁以前,不爱吃瓜的我根本不会去关注这种事,崩了就崩了呗,反正天塌下来有程序员大佬们扛着 ...
- sql 建立新表
USE [exam]GO /****** Object: Table [dbo].[tx] Script Date: 12/13/2020 22:19:59 ******/SET ANSI_NULLS ...
- JAVA基础之JDK、JRE、JVM关系
什么是JRE和JDK JDK(Java Development Kit Java开发工具包) JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE.所以安装了JDK,就不 ...
- [刘阳Java]_精选20道Java多线程面试题
1. 多线程使用的优缺点? 优点: (1)多线程技术使程序的响应速度更快 (2)当前没有进行处理的任务可以将处理器时间让给其它任务 (3)占用大量处理时间的任务可以定期将处理器时间让给其它任务 (4) ...