如何自定义一个Collector
Collectors类提供了很多方便的方法,假如现有的实现不能满足需求,我们如何自定义一个Collector呢?
Collector接口提供了一个of方法,调用该方法就可以实现定制Collector。
public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A, R> finisher,
Characteristics... characteristics) {
...
Set<Characteristics> cs = Collectors.CH_NOID;
if (characteristics.length > 0) {
cs = EnumSet.noneOf(Characteristics.class);
Collections.addAll(cs, characteristics);
cs = Collections.unmodifiableSet(cs);
}
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, finisher, cs);
}
- supplier提供一个 A 类型的对象,用于保存累加操作的结果;
- accumulator提供累加操作的实现,接收 T 类型输入数据和 A 类型对象,将计算结果保存到 A 类型对象;
- combiner用于多个输入对象的合并,在普通串行(sequential)的情况下只有一个输入对象可以忽略,假如steam使用了并发操作(parallel)时就必须进行对象合并了;
- finisher用于将输入参数类型 A 转化为我们最终需要的类型 R,假如A与R的类型一致,则无需转化,可以忽略finisher的设置;
- characteristics用于指定操作的优化类型;
值得注意的是,注释中要求提供的T对象参数的类型必须为可变的(mutable);
@param <A> the mutable accumulation type of the reduction operation (often hidden as an implementation detail)
在自定义之前,我们先来看看Collectors类源代码是怎么实现Collector接口。以常用的toList方法为例,
static final Set<Collector.Characteristics> CH_ID = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new,
List::add,
(left, right) -> {
left.addAll(right);
return left;
},
CH_ID);
}
在toList方法中,supplier为ArrayList::new,构造一个ArrayList对象用于累加的容器,使用List::add作为accumulator累加操作,combiner实现中调用List的addAll
合并两个列表,由于最终类型就是List,因此toList忽略finisher,使用IDENTITY_FINISH优化类型,标明不需要进行finisher转换类型,直接返回计算结果。
参考Collectors类方法的实现,我们来自定义一个Collector。假设有个需求,存在一个包含多个A类型对象的列表,要求计算该列表所有A对象属性count的总和。
public class A {
private int count;
public A(int count) {
this.count = count;
}
public int getCount() {
return count;
}
}
对于该需求,一般可以简单的使用mapToInt或者Collectors类提供的summingInt方法实现。
var aList = new ArrayList<A>();
...
int totalUseMap = aList.stream().mapToInt(A::getCount).sum();
// or
int totalUseCollect = aList.stream().collect(Collectors.summingInt(Obj::getCount));
但是假如使用自定义Collector的话,我们应该如何实现呢?方法如下:
var aList = new ArrayList<A>();
...
int total = aList.parallelStream().collect(Collector.of(() -> new int[1],
(result, a) -> result[0] += a.getCount(),
(a, b) -> {
a[0] += b[0];
return a;
},
result -> result[0],
Collector.Characteristics.CONCURRENT));
supplier必须提供可变类型对象,所以不能简单的提供() -> 0,因为int是不可变的类型。假如使用int类型,会导致返回结果还是提供int对象的初始值,无法完成需求。
此外计算结果类型要求为int,与提供的累加对象int[]类型不一致,因此我们通过finisher函数将int[]类型的对象转化为int类型数据返回。accumulator的计算过程并不影响并
发,设置characteristics参数为CONCURRENT,支持并发操作,提高性能。
如何自定义一个Collector的更多相关文章
- 怎么在java中创建一个自定义的collector
目录 简介 Collector介绍 自定义Collector 总结 怎么在java中创建一个自定义的collector 简介 在之前的java collectors文章里面,我们讲到了stream的c ...
- Java 8手动实现一个Collector
我们看一下Stream中的collect的方法. collect(toList())方法由Stream里的值生成一个列表,是一个及早求值的操作. Stream的of方法使用一个初始值生成新的Strea ...
- SpringMVC 自定义一个拦截器
自定义一个拦截器方法,实现HandlerInterceptor方法 public class FirstInterceptor implements HandlerInterceptor{ /** * ...
- jQuery Validate 表单验证插件----自定义一个验证方法
一.下载依赖包 网盘下载:https://yunpan.cn/cryvgGGAQ3DSW 访问密码 f224 二.引入依赖包 <script src="../../scripts/j ...
- Spring自定义一个拦截器类SomeInterceptor,实现HandlerInterceptor接口及其方法的实例
利用Spring的拦截器可以在处理器Controller方法执行前和后增加逻辑代码,了解拦截器中preHandle.postHandle和afterCompletion方法执行时机. 自定义一个拦截器 ...
- JSTL,自定义一个标签的功能案例
1.自定义一个带有两个属性的标签<max>,用于计算并输出两个数的最大值: 2.自定义一个带有一个属性的标签<lxn:readFile src=“”>,用于输出指定文件的内容 ...
- 自定义View(7)官方教程:自定义View(含onMeasure),自定义一个Layout(混合组件),重写一个现有组件
Custom Components In this document The Basic Approach Fully Customized Components Compound Controls ...
- Volley HTTP库系列教程(5)自定义一个Volley请求
Implementing a Custom Request Previous Next This lesson teaches you to Write a Custom Request parse ...
- 在String()构造器不存在的情况下自定义一个MyString()函数,实现如下内建String()方法和属性:
在String()构造器不存在的情况下自定义一个MyString()函数,实现如下内建String()方法和属性: var s = new MyString("hello"); s ...
随机推荐
- Dubbo本地存根是什么,Dubbo本地伪装又是什么?
真正的大师永远怀着一颗学徒的心 哈喽!大家好,我是小奇,一位程序员界的学徒 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 前言 书接上回,昨天打了 ...
- SQL Server之自动创建视图
本方法只适合特定模式的视图创建. 比如,创建需要整张表列名的视图,或者当表和需要的列名统计在一张数据表当中,如图所示: 首先要先获取要创建视图所需要的表,这里我获取的是整个数据库中的表, IF OBJ ...
- 7. Docker CI、CD
在上图这个新建的docker-compose.yml文件中把刚才的代码粘贴进去. 可把上述文件保存后,然后到/etc/ssh/sshd_config文件中更改下对应的端口号即可. 然后重新启动sshd ...
- swap函数模板
在许多应用程序中,都有交换相同类型的两个变量内容的需要.例如,在对整数数组进行排序时,将需要一个函数来交换两个变量的值,如下所示: void swap(int &a, int &b) ...
- Linux查看系统参数配置
Linux查看系统参数 1.查看内存(以GB为单位) [root@rac1 ~]# free -g total :内存总数,物理内存总数 used :已使用内存 free :空闲的内存数 shared ...
- 基于MybatisPlus代码生成器(2.0新版本)
一.模块简介 1.功能亮点 实时读取库表结构元数据信息,比如表名.字段名.字段类型.注释等,选中修改后的表,点击一键生成,代码成即可提现出表结构的变化. 单表快速转化restful风格的API接口并对 ...
- JQuery实现图片轮播无缝滚动
图片轮播无缝滚动实例 实现效果展示预览: 思路: 1.设置当前索引curIndex,和前一张索引prevIndex.(curIndex为下一次要显示的图片索引,prevIndex为现在看见的图片) 2 ...
- 迭代阈值收缩算法ISTA,背后的思想与具体推到过程
- springboot集成swagger2报Illegal DefaultValue null for parameter type integer
springboot集成swagger2,实体类中有int类型,会报" Illegal DefaultValue null for parameter type integer"的 ...
- 分库分表真的适合你的系统吗?聊聊分库分表和NewSQL如何选择
曾几何时,"并发高就分库,数据大就分表"已经成了处理 MySQL 数据增长问题的圣经. 面试官喜欢问,博主喜欢写,候选人也喜欢背,似乎已经形成了一个闭环. 但你有没有思考过,分库分 ...