java 8如何自定义收集器(collector)详解
作者:chauncy 发布时间:2022-02-12 07:22:17
需求:
将 一个容器List<Bean> 按照一定的字段进行分组,分组过后的值为特定的BEAN 里面的属性例如:
假定有这样一个Bean
public class SubjectOberser{
private String subjectKey;
private AbstractObserver abstractObserver;
...geter seter 方法...
}
我们需要按照 subjectKey 进行分组,分组过后的内容 应该为这样一个容器Map<String,List<AbstractObserver>>
map 中的key,为SubjectOberser 属性的subjectKey,值为List<AbstractObserver>
实现过程
首先来看看collector 的接口定义
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher();
BinaryOperator<A> combiner();
Set<Characteristics> characteristics();
}
类型 T ,是在容器里面元素的类型
类型 A ,是accumulator 返回的类型,即是累加器的返回类型
类型 R ,是最终结果的类型
supplier 方法返回的结果必须为一个空的Supplier,也就是一个空的无参函数(签名就是这样的 ()->{}),在调用的时候它会创建一个空的累加器(accumulator)实例,供数据收集的时候使用,很明显如果按照我们的需求试下你自己collector 这里应该返回一个 () -> new HashMap<>()
,一个Map 来收集结果
accumulator 方法返回归约操作的函数(签名是这样的 (a,b)->void ),当遍历到流中第n个元素时,这个函数执行时会有两个参数:保存归约结果的累加器(已 收集了流中的前n-1个项目),还有第n个元素本身。签名也展示该函数是void,因为该操作是在原来的容器里面进行更新的,所以返回的是void 类型。按照需求的中的实现应该是是这样的:
public BiConsumer<Map<String, List<AbstractObserver>>, SubjectObserver> accumulator() {
return (Map<String, List<AbstractObserver>> acc, SubjectObserver v) -> {
if (acc.containsKey(v.getSubjectKey())){
acc.get(v.getSubjectKey()).add(v.getAbstractObserver());
}else{
List<AbstractObserver> l = new ArrayList<>();
l.add(v.getAbstractObserver());
acc.put(v.getSubjectKey(),l);
}
};
}
这里的逻辑就是if else 逻辑判断就是,这个key ,在map 中是否存在,如果不存在,那么我们需要给他new一个list 的实例,不然我的的数据没有地方存储
finisher 可从名字看出方法累积过程的最后要调用的一个函数,以便将累加器对象转换为整个集合操作的最终结果。通常来说累加器的类型也是返回的结果的类型,那么就返回identity 就可以了,如果不是的话,就行自行转换了。在当前需求的情况下我们的累加器和返回结果的类型是一致的,所以这里的实现是这样的:
public Function<Map<String, List<AbstractObserver>>,
Map<String, List<AbstractObserver>>> finisher(){
return Function.identity();
}
combiner 方法是将两个累加的结果进行一个合并的过程,当然这个过程并不是每一个collector 都会调用得到(后面会讲到)
按照我们的需求,只需要将两个累加器的,中间结果合并成为一个结果即可,所以是现实这样的:
public BinaryOperator<Map<String, List<AbstractObserver>>> combiner() {
return ((Map<String, List<AbstractObserver>> map1,
Map<String, List<AbstractObserver>> map2) -> {
map1.putAll(map2);
return map1;
});
}
characteristics 该方法返回一个 Characteristics 的集合,它有如下值可选
UNORDERED—— 归约结果不受流中项目的遍历和累积顺序的影响。
CONCURRENT—— accumulator函数可以从多个线程同时调用,且该收集器可以并行执行。如果收集器没有标为UNORDERED,那 它仅在用于用于无序数据源时才可以并行归约。
IDENTITY_ FINISH—— 这表明完成器方法返回的函数是一个不改变的函数,这种情况下,累加器对象将会直接用作合并过程 的最终结果。
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
}
最终collector 代码合在一起就是:
public class MyCollector implements Collector<SubjectObserver,
Map<String, List<AbstractObserver>>,
Map<String, List<AbstractObserver>>> {
@Override
public Supplier<Map<String, List<AbstractObserver>>> supplier() {
return () -> new HashMap<>();
}
@Override
public BiConsumer<Map<String, List<AbstractObserver>>, SubjectObserver> accumulator() {
return (Map<String, List<AbstractObserver>> acc, SubjectObserver v) -> {
if (acc.containsKey(v.getSubjectKey())) {
acc.get(v.getSubjectKey()).add(v.getAbstractObserver());
} else {
List<AbstractObserver> l = new ArrayList<>();
l.add(v.getAbstractObserver());
acc.put(v.getSubjectKey(), l);
}
};
}
@Override
public BinaryOperator<Map<String, List<AbstractObserver>>> combiner() {
return ((Map<String, List<AbstractObserver>> map1, Map<String, List<AbstractObserver>> map2) -> {
map1.putAll(map2);
return map1;
});
}
@Override
public Function<Map<String, List<AbstractObserver>>, Map<String, List<AbstractObserver>>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
}
}
调用的过程就是:
public static Map<String, List<AbstractObserver>> initObjectMap() {
ClassScaner classScaner = new ClassScaner();
Set<Class> set = classScaner.doScan("com.souche.datacenter.observer");
return set
.stream()
.filter(aClass -> SubjectAnnotationResolver.getAnnotationSubjectName(aClass) != null)
.map(aClass -> {
String subjectKey = SubjectAnnotationResolver.getAnnotationSubjectName(aClass);
AbstractObserver abstractObserver = getBeanByClassName(aClass.getSimpleName());
return new SubjectObserver(subjectKey, abstractObserver);
}).collect(new MyCollector());
}
直接在使用的地方直接new MyCollector
就可以了
总结


猜你喜欢
- SpringBoot对actuator进行关闭management: endpoint: health
- 前言:Zygote 是 Android 的核心,每打开一个 app,Zygote 就会 fork 一个虚拟机实例来运行 app,基于Xpos
- Android SimpleAdapter使用详解HolderAdapter背景Android的AdapterView用的比较多,ListV
- 前言在介绍Dubbo之前先了解一下基本概念:Dubbo是一个RPC框架,RPC,即Remote Procedure Call(远程过程调用)
- 首先说说什么叫回调函数?在WINDOWS中,程序员想让系统DLL调用自己编写的一个方法,于是利用DLL当中回调函数(CALLBACK)的接口
- 背景:听说ClassLoader类加载机制是进入BAT的必经之路。ClassLoader总述:普通的Java开发其实用到ClassLoade
- 基本的SpringMVC的搭建在我的上一篇文章里已经写过了,这篇文章主要说明一下如何使用SpringMVC进行表单上的文件上传以及多个文件同
- 本次和大家分享的是怎么来消费服务,上篇文章讲了使用Feign来消费,本篇来使用rest+ribbon消费服务,并且通过轮询方式来自定义了个简
- 为什么需要Spring MVC最开始接触网页的时候,是纯的html/css页面,那个时候还是用Dreamweaver来绘制页面。随着网站开发
- 本文实例讲述了C#使用委托(delegate)实现在两个form之间传递数据的方法。分享给大家供大家参考。具体分析如下:关于Delegate
- 前言若你的工程还没有进行基础配置,请查看我的博文Unity 之 ShaderGraph入门使用详解,按照步骤操作完成配置即可,还能顺便学习一
- 1、有状态的bean与无状态的bean有状态bean:每个用户有自己特有的一个实例,在用户的生存期内,bean保存了用户的信息,即有状态;一
- Eureka注册中心/服务发现框架Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中
- 本文实例讲述了C#基于socket模拟http请求的方法。分享给大家供大家参考。具体实现方法如下:using System;using Sy
- 背景在我们实际生产容器化部署过程中,往往会遇到 Docker 镜像很大,部署发布很慢的情况影响 docker 镜像大小的因素,主要有以下三个
- OO思想现在已经在软件开发项目中广泛应用,其中最重要的一个特性就是继承,最近偶简单的复习了下在C#中涉及到继承这个特性时,所需要用到的关键字
- 教你如何用C#制作文字转换成声音程序在System.Speech命名空间下,SpeechSynthesizer类可以把文字读出来,一起来玩下
- 如何实现使用TextView的DrawableLeft使图片和文字居中显示呢???代码如下: 1.首先自定义一个类,继承TextViewpa
- 本文实例讲述了Java Spring开发环境搭建及简单入门示例。分享给大家供大家参考,具体如下:前言虽然之前用过Spring,但是今天试着去
- Android中ListView下拉刷新实现效果图:ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理