StreamAPI源码分析之一(Colector收集器)

前言

前面介绍JAVA8之Stream API使用介绍一、二、三的时候有使用过collect(toList())收集函数。接下来分析的Colector收集器与collect(toList())收集函数有着密不可分的关系。

1、什么是collect()函数

collect()函数是及早求值操作,参数是收集器Collector(Collector是一个接口,需要进行实现,当然JDKAPI已经为我们实现了一些通用的Collector了,如果需要自己需求的Collector,需要自定义)

Stream<String> stream = Stream.of("hello", "world", "helloworld");
                List<String> list = stream.collect(Collectors.toList());
                List<String> list1 = stream.collect(
                        () -> new ArrayList(), 
                        (theList, item) -> theList.add(item),
                        (theList1, theList2) -> theList1.addAll(theList2));
        
        List<String> list2 = stream.collect(LinkedList::new, LinkedList::add, LinkedList::addAll);
        list.forEach(System.out::println);
        //定制返回的容器toCollection()方法
        List<String> list = stream.collect(Collectors.toCollection(ArrayList::new));
        Set<String> set = stream.collect(Collectors.toCollection(TreeSet::new));

collect()函数有toList()、toSet()、toCollection()三种直接创建集合的方式。更详细的信息,请查看Collectors静态工厂类

2、Colector收集器

Colector收集器(理解函数式思维的关键) 更详细的说明请查看Colector接口文档(如果看完列完的定义不明白一定要去看Colector接口文档)

  • 1、它是一个接口,它是一个可变的汇聚操作,将输入元素累积到一个可变的结果容器中,它会在所有元素都处理完毕后,将累积的结果转换成一个最终目标(这是一个可选操作)
    ,它支持串行和并行方式执行。
  • 2、Collectors本身提供了关于Collector的常见汇聚实现,Collectors其实就是一个工厂。
  • 3、collector由四个函数指定,这些函数一起工作,将条目累积到可变结果容器中,并可以选择对结果执行最终转换。
  • 4、为了确保串行和并行操作结果的等价性,collector函数需要满足两个条件:identity(同一性)与associativity(结合性)
  • 5、a=combiner.apply(a, supplier.get()); a是一个部分结果
  • 6、函数式编程最大的特点:表示做什么,而不是如何做
    例如:
(list1,list2)->{list1.addAll(list2);return list1;}

colletor包含的核心变量有:

        They are: <ul>
         *     <li>creation of a new result container ({@link #supplier()})</li>
         *     <li>incorporating a new data element into a result container ({@link #accumulator()})</li>
         *     <li>combining two result containers into one ({@link #combiner()})</li> 
         *     <li>performing an optional final transform on the container ({@link #finisher()})</li>
         * </ul>

对 Collector三个泛型参数进行解释
public interface Collector<T, A, R>

  • @param T 还原操作的输入元素类型

  • @param A 还原操作的可变累积类型(通常作为实现细节*隐藏)

  • @ param R 还原操作的结果类型

    colletor接口中的四个五个变量:
  • 1、supplier函数,生成新容器

  • 2、accumulator函数,对于数据两两进行操作,合并成一个放入容器中

  • 3、combiner函数(只有多线程使用,串行流不会使用),如果有四个线程同时执行,那么就会生成四个部分结果. 1,2,3,4,集合进行合并

  • 4、finisher函数:将accumulator函数结果或者是combiner函数结果转换成指定的类型

  • 5、 characteristics特性值:流计算特性进行指定

colletor的内部枚举类(对于流计算的特性进行设置,完成我们需要的功能)
 /**
     *表示{@code collector}属性的特征,该属性可*用于优化精简实现。
     */
    enum Characteristics {
        /**
         *指示此收集器是并发的,这意味着*结果容器可以支持与来自多个		  *线程的同一结果容器同时*调用的累加器函数。
         * 
         *<p>如果{@code concurrent}收集器也不是{@code unordered},*则只应在应用于*无序数据源时并发计算。
         */
        CONCURRENT,

        /**
         * 指示集合操作不承诺保留*输入元素的相遇顺序。(如果*结果容器没有内在顺序,例如{@link set},则可能是这样的。)
         */
        UNORDERED,

        /**
         * 表示分页装订器函数是标识函数,*可以省略。如果设置了,则从a到r的未检查强制转换*必须成功。
         */
        IDENTITY_FINISH
    }

3、自定义自己的colletor定制器

public class MySetCollector2<T> implements Collector<T, Set<T>, Map<T, T>> {

    @Override
    public Supplier<Set<T>> supplier() {
        System.out.println("supplier invoked!");
		 return HashSet<T>::new;
    }

    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        System.out.println("accumulator invoked!");

        return (set, item) -> {
            System.out.println("accumulator: " +set+", "+ Thread.currentThread().getName());
            set.add(item);
        };
    }

    @Override
    public BinaryOperator<Set<T>> combiner() {
        System.out.println("combiner invoked!");
        return (set1, set2) -> {
            set1.addAll(set2);
            return set1;
        };
    }

    @Override
    public Function<Set<T>, Map<T, T>> finisher() {
        System.out.println("finisher invoked!");
        return set -> {
            Map<T, T> map = new TreeMap<>();
            set.stream().forEach(item -> map.put(item, item));
            return map;
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        System.out.println("characteristics invoked!");
        return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED, Characteristics.CONCURRENT));
    }

    public static void main(String[] args) {

        System.out.println(Runtime.getRuntime().availableProcessors());

        for(int i = 0; i < 99; ++i) {

            List<String> list = Arrays.asList("hello", "world", "welcome", "hello", "a",
                    "b", "c", "d", "e", "f", "g");
            Set<String> set = new HashSet<>();
            set.addAll(list);
            System.out.println("set: " + set);
            Map<String, String> map = set.parallelStream().collect(new MySetCollector2<>());
            System.out.println(map);
        }

    }
}

其实自定义收集器的话,我本人在工作中用的也是比较少的,基本上JDK自带的功能都能完成需求开发,这里就是对它进行理解,刚开始理解的位置那激就是知道里面有四个函数式接口,一个特性,完成四个函数式接口完成不同的任务,可以流Stream指定运行特性这样子可能理解起来更简单点。

这里再给大家加个餐,通过示例在理解一下收集器:
jdk8-Collector收集器之并行流陷阱与原理link

总结

本节主要对collector进行了介绍并进行分析,理解collector是对流计算及其重要的一环,希望博客可以帮到更多的人。
下一节为StreamAPI源码分析之二(Colectors工厂类)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值