手撕Java类 Collector

本文并没有深挖 Collector 在jdk源码中的整个流程,只是介绍如何构造 Collector

Collector在 Stream 中的使用

Collector 用作 Stream类的归约操作 collect方法的参数;collect()有着两个方法签名,如下:

<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
// 收集器Collector 对上面的三个参数进行了封装,但并不仅仅是这样
<R, A> R collect(Collector<? super T, A, R> collector);
  • supplier 用于提供返回的结果容器(指可变容器);
  • accumulator 用于如何将值放入一个结果容器中;
  • combiner 用于并行情况下如何合并两个结果容器;

验证代码如下:

// 测试 第三个参数 combiner 是否只在并行流情况下执行
        IntStream range = IntStream.range(0, 10);
        range.collect(ArrayList::new, List::add, (list1, list2) -> {
            System.out.println(Thread.currentThread().getName()+"执行了");
            list1.addAll(list2);
        });
        System.out.println("并行流:");
        AtomicInteger count = new AtomicInteger(1);
        IntStream.range(0, 10).parallel().collect(ArrayList::new, List::add, (list1, list2) -> {
            System.out.println(Thread.currentThread().getName()+"执行了"+count.getAndDecrement());
            list1.addAll(list2);
        });
        System.out.println("count: "+ count.get());

Collector 接口

collector 接口提供五个抽象方法,分别返回了 supplier、accumulator、combiner、finisher和characteristics。

前三者的和之前介绍的一样,不过combiner 是有区别的,这里的 combiner 是一个 BinaryOperator,也就是说它除了指定如何在并发环境下如何操作两个结果容器,还允许指定返回的结果容器;finisher 是对结果容器最后的转换;characteristics 是 collector 的属性,能够优化归约操作;结合下面代码理解:

 Stream<String> stream1 = Stream.of("I", "Love", "You", "!");
        List<String> result = stream1.collect((Supplier<List<String>>)ArrayList::new, List::add, (list1, list2) -> list1.addAll(list2));
        result.forEach(System.out::println);
        System.out.println("使用Collectors:");
        Stream<String> stream2 = Stream.of("I", "Love", "You", "!");
        List<Integer> result2 = stream2.collect(new Collector<String, List<String>, List<Integer>>() {
            @Override
            public Supplier<List<String>> supplier() {
                return ()->{
                    System.out.println("产生容器:list");
                    return new ArrayList<>();
                };
            }

            @Override
            public BiConsumer<List<String>, String> accumulator() {
                return (list,s)->{
                    System.out.println("往容器中添加元素");
                    list.add(s);
                };
            }

            @Override
            public BinaryOperator<List<String>> combiner() {
                return (list1, list2) -> {
                    System.out.println("合并结果集");
                    list1.addAll(list2);
                    return list1;
                };
            }

            @Override
            public Function<List<String>, List<Integer>> finisher() {
                return r1->{
                    System.out.println("我将追加后缀:hha");
                    List<Integer> r2 = new ArrayList<>();
                    r2.add(r1.size());
                    return r2;
                };
            }

            /**
             * 如果在这里指定了 characteristics 集包含了 IDENTITY_FINISH,那么将不会执行 finisher方法
             * 由于泛型的原因,仍然能够返回 List<Integer> ,最终打印的时候会报类型转换错误
             * @author duofei
             * @date 2019/7/27
             * @return void
             */
            @Override
            public Set<Characteristics> characteristics() {
                //return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
                return new HashSet<>();
            }
        });
        result2.forEach(System.out::println);

总结

使用 Collector 进行归约操作,需要指定返回的结果容器 ( supplier),怎样往结果容器中添加元素(accumulator),以及如何在并发情况下,合并结果容器(combiner),除此之外,还提供了对结果容器进行再处理的方法(finisher),除此之外,还需要指定属性(characteristics),来优化归约操作。

Collectors 类提供了一系列 Collector,例如:

// 具体参考源码,和上述的构建一样
public static <T>
    Collector<T, ?, Set<T>> toSet() {
        return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_UNORDERED_ID);
    }
    
// 如果我们自己写,可能是这样
BinaryOperator<Set<T>> combiner = (Set<T> left, Set<T> right) -> {
            left.addAll(right);
            return left;
        };
        Collector<T, ?, Set<T>> toSet = Collector.of(HashSet::new, Set::add,combiner,
                Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH);

现在,除了使用 Collectors 提供的Collector 以外,可以自己写更符合使用场景的 Collector了。

如果你觉得我的文章对你有所帮助,欢迎关注我的公众号。赞!我与风来
认认真真学习,做思想的产出者,而不是文字的搬运工。错误之处,还望指出!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值