• 【java8】自定义Collector


    Collector接口包含了一系列方法,为实现具体的归约操作(即收集器)提供了范本。我们已经看过了Collector接口中实现的许多收集器(由Collector接口的工具类Collectors提供),例如toList()或groupingBy()。

    这也意味着你可以为Collector接口提供自己的实现,从而自由地创建自定义归约操作。

    要实现自定义收集器,只需要实现java.util.stream.Collector<T, A, R>接口即可.

    Collector接口的声明如下:

    public interface Collector<T, A, R> {
    
        Supplier<A> supplier();
    
        BiConsumer<A, T> accumulator();
    
        BinaryOperator<A> combiner();
    
        Function<A, R> finisher();
    
        Set<Characteristics> characteristics();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    泛型介绍

    • T:stream在调用collect方法收集前的数据类型

    • A:A是T的累加器,遍历T的时候,会把T按照一定的方式添加到A中,换句话说就是把一些T通过一种方式变成A

    • R:R可以看成是A的累加器,是最终的结果,是把A汇聚之后的数据类型,换句话说就是把一些A通过一种方式变成R

    接口介绍

    • supplier: 怎么创建一个累加器

    • accumulator:怎么把一个对象添加到累加器中

    • combiner: 怎么把一个累加器和另一个累加器合并起来,此方法并行时才会调用

    • finisher: 怎么把A转化为R

    • characteristics: 特征值,告诉collect方法在执行归约操作的时候可以应用哪些优化

    Characteristics

    包含三个项目的枚举:

    • UNORDERED:归约结果不受流中项目的遍历和累积顺序的影响

    • CONCURRENT:accumulator函数可以从多个线程同时调用,且该收集器可以并行归约流。如果收集器没有标为UNORDERED, 那它仅在用于无序数据源时才可以并行归约。

    • IDENTITY_FINISH:这表明完成器方法返回的函数是一个恒等函数,可以跳过。这种情况下,累加器对象将会直接用做归约过程的最终结果。这也意味着,将累加器A不加检查地转换为结果R是安全的。

    当Collector设置为IDENTITY_FINISH,finisher方法不会调用,因为不用再类型转换了,中间数据类型就是最终的数据类型。

    Stream#collect()源码分析

    下面的Stream的实现类ReferencePipeline的collect方法的源码

    public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) { // @1
    	A container;
    	if (isParallel()
    		&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
    		&& (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) { // @2
    		container = collector.supplier().get(); // @3
    		BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
    		forEach(u -> accumulator.accept(container, u));
    	}
    	else {
    		container = evaluate(ReduceOps.makeRef(collector)); // @4
    	}
    	return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
    		? (R) container
    		: collector.finisher().apply(container); // @5
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 代码@1:函数声明,该方法返回的结果类型为R,传入的行为参数接口为Collector。

    • 代码@2:判断是否符合并行化累积与规约的条件。

      • 是否是并行流,Stream.stream()方法的流是非并行化流,如果要支持并行化执行,需要使用Stream.parallelStream()方法。

      • Collector(收集器,行为化参数)中收集器行为集合中是否包含Characteristics.CONCURRENT(并行执行),如果不包含该行为,则不支持并行执行。

      • 原始流是否有顺序或者收集器的行为集合中明确包含Characteristics.UNORDERED(不要求顺序性)。

      • 上述三个条件必须同时满足,才能并行执行,否则串行执行。

    • 代码@3:并行执行收集动作。

    • 代码@4:串行执行收集动作。

    • 代码@5:如果收集器收集行为集合中包含Characteristics.IDENTITY_FINISH,则直接返回原始值,否则使用Collector.finishier()方式对计算的值进行函数式计算。

    自定义toList

    package com.morris.java8.collector;
    
    import java.util.*;
    import java.util.function.BiConsumer;
    import java.util.function.BinaryOperator;
    import java.util.function.Function;
    import java.util.function.Supplier;
    import java.util.stream.Collector;
    
    public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {
    
        @Override
        public Supplier<List<T>> supplier() {
            return ArrayList::new;
        }
    
        @Override
        public BiConsumer<List<T>, T> accumulator() {
            return List::add;
        }
    
        @Override
        public BinaryOperator<List<T>> combiner() {
            return (left, right) -> {
                left.addAll(right);
                return left;
            };
    
        }
    
        @Override
        public Function<List<T>, List<T>> finisher() {
            return Function.identity();
        }
    
        @Override
        public Set<Characteristics> characteristics() {
            return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
        }
    
        public static void main(String[] args) {
            List<Dish> dishList = Dish.createList().stream().filter(Dish::isVegetarian).collect(new ToListCollector<>());
            System.out.println(dishList);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    自定义joining

    package com.morris.java8.collector;
    
    import java.util.*;
    import java.util.function.BiConsumer;
    import java.util.function.BinaryOperator;
    import java.util.function.Function;
    import java.util.function.Supplier;
    import java.util.stream.Collector;
    
    public class JoiningCollector implements Collector<String, StringBuilder, String> {
    
    
        private String seperator = ",";
    
        public JoiningCollector() {
        }
    
        public JoiningCollector(String seperator) {
            this.seperator = seperator;
        }
    
        @Override
        public Supplier<StringBuilder> supplier() {
            return StringBuilder::new;
        }
    
        @Override
        public BiConsumer<StringBuilder, String> accumulator() {
            return (sb, str) -> sb.append(str).append(seperator);
        }
    
        @Override
        public BinaryOperator<StringBuilder> combiner() {
            return StringBuilder::append;
        }
    
        @Override
        public Function<StringBuilder, String> finisher() {
            return c -> {
                String ret = c.toString();
                if (ret.endsWith(seperator)) {
                    return ret.substring(0, ret.length() - 1);
                }
                return ret;
            };
        }
    
        @Override
        public Set<Characteristics> characteristics() {
            return new HashSet<>();
        }
    
        public static void main(String[] args) {
            String collect = Arrays.asList("hello", "world", "java", "stream").stream().collect(new JoiningCollector("|"));
            System.out.println(collect);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
  • 相关阅读:
    python机器学习——决策树
    jenkins实践篇(2)—— 自动打tag的可回滚发布模式
    ArcGIS Pro 优化的热点分析【Optimized Hot Spot Analysis】
    20231018 自然常数的存在性
    和为S的连续正数序列
    MAC M1安装多个JDK版本及动态切换
    关于在WPF xaml中包含另一个window的方法
    1.4 Financial Disasters
    如何开发新客户?有哪些高效率方法?
    手机照片回收站无法恢复图片怎么办?2个措施,找回丢失的相册
  • 原文地址:https://blog.csdn.net/u022812849/article/details/125506974