Java8 Stream 自定义Collector

简介: Java8 Stream 自定义Collector

在之前的例子中,我们都是使用Collectors的静态方法提供的CollectorImpl,为接口Collector的一个实现类,为了自定义我们自己的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执行规约过程


image.png

二、自定义一个功能与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_FINISHCONCURRENT组合,当数据源为无序集合时,CONCURRENT才会体现出作用。


相关文章
|
1月前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
59 1
|
1月前
|
Java
在 Java 中,如何自定义`NumberFormatException`异常
在Java中,自定义`NumberFormatException`异常可以通过继承`IllegalArgumentException`类并重写其构造方法来实现。自定义异常类可以添加额外的错误信息或行为,以便更精确地处理特定的数字格式转换错误。
34 1
|
2月前
|
Java
让星星⭐月亮告诉你,自定义定时器和Java自带原生定时器
定时器是一种可以设置多个具有不同执行时间和间隔的任务的工具。本文介绍了定时器的基本概念、如何自定义实现一个定时器,以及Java原生定时器的使用方法,包括定义定时任务接口、实现任务、定义任务处理线程和使用Java的`Timer`与`TimerTask`类来管理和执行定时任务。
58 3
|
11天前
|
Java
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
73 34
|
19天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
35 6
|
19天前
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
10天前
|
Rust 安全 Java
Java Stream 使用指南
本文介绍了Java中Stream流的使用方法,包括如何创建Stream流、中间操作(如map、filter、sorted等)和终结操作(如collect、forEach等)。此外,还讲解了并行流的概念及其可能带来的线程安全问题,并给出了示例代码。
|
1月前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
56 14
|
21天前
|
安全 Java API
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
24 0
|
1月前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。