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 接口方法

  1. 建立新的结果容器:supplier方法

    supplier方法需返回一个Supplier,也就是一个无参数函数。

    在调用时会创建一个空的累加器实例,供数据收集过程使用。

    如Collectors.toList()中supplier实现为:

    return ArrayList:new;

  2. 将元素添加到结果容器:accumulator方法

    accumulator方法会返回执行规约操作的函数,每次执行函数都会更新累加器。

    BiConsumer 无返回值,原位更新累加器。两个参数分别为保存规约结果的累加器和遍历元素。

    如Collectors.toList()中accumulator方法实现为:

    return List:add;

  3. 对结果容器应用最终转换:finisher方法

    这是在遍历完流之后,在累积过程的最后要调用的一个函数,以便将累加器对象转换为整个集合操作的最终结果。

    通常,累加器对象便是最终结果。如Collectors.toList()方法中finisher实现为:

    return List:addAll;

  4. 合并两个结果容器:combiner方法

    返回一个供规约操作使用的函数,定义了对流的各个子部分进行并行处理时,各个子部分要如何合并。

    即将多个累加器合并为一个,如Collectors.toList()中combiner实现为:

    return List:addALL

  5. 定义收集器的行为: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的更多相关文章

  1. java stream collector

    Java Stream API进阶篇 本文github地址 上一节介绍了部分Stream常见接口方法,理解起来并不困难,但Stream的用法不止于此,本节我们将仍然以Stream为例,介绍流的规约操作 ...

  2. java stream中Collectors的用法

    目录 简介 Collectors.toList() Collectors.toSet() Collectors.toCollection() Collectors.toMap() Collectors ...

  3. 深度掌握 Java Stream 流操作,让你的代码高出一个逼格!

    概念 Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选.排序.聚合等. Stream 的操作符大体上分为两种:中间操作符和终止操作符 中 ...

  4. Java Stream 使用详解

    Stream是 Java 8新增加的类,用来补充集合类. Stream代表数据流,流中的数据元素的数量可能是有限的,也可能是无限的. Stream和其它集合类的区别在于:其它集合类主要关注与有限数量的 ...

  5. Java Stream函数式编程图文详解(二):管道数据处理

    一.Java Stream管道数据处理操作 在本号之前发布的文章<Java Stream函数式编程?用过都说好,案例图文详解送给你>中,笔者对Java Stream的介绍以及简单的使用方法 ...

  6. [源码解析] 当 Java Stream 遇见 Flink

    [源码解析] 当 Java Stream 遇见 Flink 目录 [源码解析] 当 Java Stream 遇见 Flink 0x00 摘要 0x01 领域 1.1 Flink 1.2 Java St ...

  7. Java Stream 源码分析

    前言 Java 8 的 Stream 使得代码更加简洁易懂,本篇文章深入分析 Java Stream 的工作原理,并探讨 Steam 的性能问题. Java 8 集合中的 Stream 相当于高级版的 ...

  8. Java Stream API性能测试

    已经对Stream API的用法鼓吹够多了,用起简洁直观,但性能到底怎么样呢?会不会有很高的性能损失?本节我们对Stream API的性能一探究竟. 为保证测试结果真实可信,我们将JVM运行在-ser ...

  9. java stream 原理

    java stream 原理 需求 从"Apple" "Bug" "ABC" "Dog"中选出以A开头的名字,然后从中选 ...

随机推荐

  1. excel判断数据是否存在另一列中

    1.if(EXACT(A2,B2)=TRUE,"相同","不同"),A2,B2相同(字母区分大小写)则函数值true正确,反馈相同,反之返回不同.注:单元格值受 ...

  2. 10、linux启动过程

    (1)linux启动说明: 第一步:开机自检,检查硬件,加载BIOS(帮我们找到启动盘是谁): 第二步:读取MBR(读取启动硬盘0柱面0磁道1扇区(512字节)的前446字节,找到装有操作系统的分区) ...

  3. 校门外的树 (Vijos P1448)

    题目链接 描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的-- 如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作: K=1,K=1 ...

  4. Linux:获取Linux离线postgresql数据库安装包并部署

    获取离线安装包(方法一) 1. 进入官网:https://www.postgresql.org/ 2. 下载步骤 (1)点击Download (2) 选择安装的服务器的版本  (3)更新yum源 #更 ...

  5. 第一章 - Java与线程

    目录 01.Java和线程 02.Java与协程 03.CPU缓存结构和java内存模型 04.管程-悲观锁和锁优化 05.乐观锁 - 无锁方案 06.线程安全 07.线程池 08.JUC 09.高并 ...

  6. vim编辑器使用方法(相关指令)

    1.跳到文本的最后一行:按"G",即"shift+g" 2.跳到最后一行的最后一个字符 : 先重复1的操作即按"G",之后按"$& ...

  7. buu crypto 变异凯撒

    一.由题目就可知是凯撒加密,但是是变异,说明有改动,但是凯撒的本质移位是不变的,将密文afZ_r9VYfScOeO_UL^RWUc,和flag进行比较,字符表查一下,发现 a:97 f:102 f:1 ...

  8. C语言代码段

    /* 功 能:将str字符串中的oldstr字符串替换为newstr字符串 * 参 数:str:操作目标 oldstr:被替换者 newstr:替换者 * 返回值:返回替换之后的字符串 */ char ...

  9. python使用笔记11--时间模块

    1.时间模块常用方法 1 import time,datetime 2 #格式化好的时间2020-05-16 18:30:52 3 #时间戳1589616753 从unix元年(计算机发明的时间)到现 ...

  10. 支持 Homebrew 安装和编辑器模式的 flomo 命令行工具

    什么是 flomo-cli 这是一款可以在命令行中将笔记和想法保存到 flomo 的工具. 基于 Golang 实现,可通过 Homebrew 便捷安装. GitHub Repo:https://gi ...