Java8 Stream 自定义收集器Collector
时间:2022-07-26
本文章向大家介绍Java8 Stream 自定义收集器Collector,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
在之前的例子中,我们都是使用Collectors的静态方法提供的CollectorImpl
,为接口Collector<T, A, R>
的一个实现类,为了自定义我们自己的Collector,先来分析一下Collector接口。
一、分析接口Collector
/**
* @param <T> 要收集的元素的泛型
* @param <A> 累加器容器的类型,
* @param <R> 收集操作得到的对象类型
* @author futao
* @date 2020/9/24
*/
public class MyCustomCollector<T, A, R> implements Collector<T, A, R> {
/**
* 创建一个接收结果的可变容器
*
* @return a function which returns a new, mutable result container
*/
@Override
public Supplier<A> supplier() {
return null;
}
/**
* 将流中的元素放入可变容器中的逻辑, 方法
*
* @return a function which folds a value into a mutable result container
*/
@Override
public BiConsumer<A, T> accumulator() {
return null;
}
/**
* 组合结果,当流被拆分成多个部分时,需要将多个结果合并。
*
* @return a function which combines two partial results into a combined
* result
*/
@Override
public BinaryOperator<A> combiner() {
return null;
}
/**
* 最后调用:在遍历完流后将结果容器A转换为最终结果R
*
* @return a function which transforms the intermediate result to the final
* result
*/
@Override
public Function<A, R> finisher() {
return null;
}
/**
* 返回一个描述收集器特征的不可变集合,用于告诉收集器时可以进行哪些优化,如并行化
*
* @return an immutable set of collector characteristics
*/
@Override
public Set<Characteristics> characteristics() {
return null;
}
}
- 描述收集器的特征:
enum Characteristics
enum Characteristics {
/**
* 意味着结果容器支持多线程并发操作accumulator()方法向容器中放入元素。
* 如果收集器没有被同时标记为无序的`UNORDERED`,则该特征只在数据源为无序(如set)时才有效
*/
CONCURRENT,
/**
* 规约操作不保证集合中元素的顺序
* 如结果容器为Set的场景下
*/
UNORDERED,
/**
* 表明`完成方法finisher`是一个恒等式,可以被忽略
* 累加器的结果combiner A 将会作为最终的结果R
* 这也要求,直接将A转换成R是安全的
*/
IDENTITY_FINISH
}
- Collectors中常见的
Characteristics
组合
static final Set<Collector.Characteristics> CH_CONCURRENT_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_CONCURRENT_NOID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED));
static final Set<Collector.Characteristics> CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_UNORDERED_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
- Collector执行规约过程
图片来源:《java8 in action》
二、自定义一个功能与Collectors.toList()
一致的Collector
/**
* 自定义收集器
*
* @author futao
* @date 2020/9/24
*/
public class MyCollectors {
private MyCollectors() {
}
/**
* 描述:将流中的元素转换成List<T>输出
*
* Collector三个泛型:
* <入参泛型类型,
* 中间结果容器类型(在这个例子中为List<T>),
* 最终结果容器类型(这个例子中也是List<T>)>
* @param <T>
*/
public static class ToList<T> implements Collector<T, List<T>, List<T>> {
/**
* 创建一个接收结果的可变容器
* 即:创建一个List<T>对象的方法
*
* @return
*/
@Override
public Supplier<List<T>> supplier() {
return ArrayList::new;
}
/**
* 将流中的元素放入可变容器中的方法
*
* @return
*/
@Override
public BiConsumer<List<T>, T> accumulator() {
// return (list, item) -> list.add(item);
return List::add;
}
/**
* 组合结果,当流被拆分成多个部分时,需要将多个结果合并。
*
* @return
*/
@Override
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
};
}
/**
* 最后调用:在遍历完流后将结果容器A转换为最终结果R
* 在该例子中,combiner结果可作为最终结果,所以返回一个恒等式
*
* @return
*/
@Override
public Function<List<T>, List<T>> finisher() {
// return x -> x;
return Function.identity();
}
/**
* 返回一个描述收集器特征的不可变集合
* 该例子中可用的特性是:
* finisher可跳过,直接将combiner结果返回。
* 需要保证有序
* 不可并发
*
* @return
*/
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
}
}
}
- 测试
public static void main(String[] args) {
Apple chinaApple = new Apple(10, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
Apple japanApple = new Apple(40, "日本");
List<Apple> collect = Stream.of(chinaApple, usApple, koreaApple, japanApple)
// 使用自定义的收集器
.collect(new MyCollectors.ToList<>());
System.out.println(JSON.toJSONString(collect, true));
}
- 结果
[
{
"country":"中国",
"weight":10
},
{
"country":"米国",
"weight":20
},
{
"country":"韩国",
"weight":30
},
{
"country":"日本",
"weight":40
}
]
三、 当finisher为恒等式时的简便写法
List<Apple> simpleListApples = Stream.of(chinaApple, usApple, koreaApple, japanApple)
// 对于finisher为恒等式,可以用这种简单的写法
.<List<Apple>>collect(ArrayList::new, List::add, List::addAll);
System.out.println(JSON.toJSONString(simpleListApples, true));
- 使用这种方式定义的Collector收集器默认的特性是
IDENTITY_FINISH
和CONCURRENT
组合,当数据源为无序集合时,CONCURRENT才会体现出作用。
欢迎在评论区留下你看文章时的思考,及时说出,有助于加深记忆和理解,还能和像你一样也喜欢这个话题的读者相遇~
- jackson error 含义log
- java删除文件夹
- JSP自定义tag
- gradle中使用嵌入式(embedded) tomcat, debug 启动
- spring in action 4th --- quick start
- Date, TimeZone, MongoDB, java中date的时区问题
- spring boot 添加拦截器
- spring boot 部署为jar
- 重定向Http status code 303 和 302
- centos7查看系统版本,查看机器位数x86-64
- 在centos7中添加一个新用户,并授权
- 如何优化coding
- 在PowerShell中使用curl(Invoke-WebRequest)
- linux centos中添加删除修改环境变量,设置java环境变量
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 常用功能加载宏——拆分工作表
- MyVBA加载宏——添加自定义菜单04——功能实现
- CS学习笔记 | 14、powerup提权的方法
- VBA解压缩ZIP文件05——Huffman树
- JavaScript|jQuery基础语法
- VBA解压缩ZIP文件03——解压准备工作
- VBA解压缩ZIP文件04——解析ZIP文件结构
- 开发|Springboot简单实现文件上传
- VBA解压缩ZIP文件01——实现的功能
- MyVBA加载宏——添加自定义菜单03——功能分析
- MyVBA加载宏——添加自定义菜单02——给按钮添加单击事件
- 科研猫小课堂:敲黑板!竞争风险模型应该如何分析?
- 常用功能加载宏——快速定位合并单元格
- 常用功能加载宏——调用微信截图
- 常用功能加载宏——一维表转二维表