Collector

list.stream().map(i -> i * 2).collect(Collectors.toList);

  • collect是stream中的收集器
  • Collector是collect方法中的参数,Collector是一个接口
  • collect是一个可变的汇聚操作,将输入元素累积到一个可变的结果容器中。
  • Collector当所有的元素都处理完毕后,将累积的结果转化为一个最终的表示(这是一个可选操作)
  • Collector同时支持串行和并行操作
  • Collectors本身提供了Collector的常见汇聚实现,Collectors本身实际上是一个工厂。
  • Collector是由四个功能指定一起工作以累加条目到一个可变的结果容器,和任选地执行该结果的最终变换:

    • 创建一个新的结果容器: supplier()

      • 如果是并行流,则会在每个分区都创建一个结果容器。
    • 结合有新的数据元素到结果容器: accumulator()

      • 每个元素只会调用accumulator()一次
    • 两个结果的容器组合成一个: combiner()

      • Combiner是由BinaryOpreator实现,只用在并行流情况下使用(串行流只会有一个结果,无需combine)
      • combiner的逻辑:假设有1,2,3,4号线程处理,返回了4个结果,有可能出现的组合方式是:

        • 1,2合并,返回5
        • 1,2合并,返回1,1中的元素addAll了2
    • 执行任选的最终容器上变: finisher()
    • Characteristics是一个枚举类,来判断结果集合容器是否支持并行操作。
    • 为了保证并发安全,保证并行和串行执行的结果一致,收集器函数必须满足两个条件

      • 同一性(identity):

        • 假设a为其中一个部分累积的结果,那么a必须满足:a == combiner.apply(a, supplier.get())
        • 结合性(associativity):分割计算必须得到一个等价的结果

          • 如果是UNORDERED,则只需要两个结果集中的内容是相同的,无视顺序。
      • 实现基于汇聚操作的Collector ,如Stream.collect(Collector) ,必须遵循以下限制:

        • 传递给accumulator()的第一个参数,以及传递给combiner()的两个参数,并传递给finisher()的参数(这3种其实都是结果容器的类型),每一次的结果容器类型必须和上一次的结果容器类型相同。
        • 实现不应该做与任何accumulator()combiner()finisher()的结果相关的任何操作。
        • 如果结果传给finisher(),相同的对象没有从函数返回的(说明你使用了新的结果容器,jdk认为之前的结果容器已经被使用完成了),它永远不会再使用。
        • 一旦结果传给combiner()finisher(),说明accumulator()已经被执行完成了,则不会再调用它。
        • 对于非并发收集器的,任何结果从supplier()accumulator()combiner()的返回必须串行线程限制,保证不会有其他线程能够获取。 通过线程封闭来实现,Collector就不需要实现任何额外的同步。 减少执行必须管理输入正确分区,该分区在隔离处理,并在combine完成后,才执行finsher操作。
        • 对于并发收集器,可以实现自由的选择并发的汇聚操作(但不是必须),即不需要使用隔离,所有的线程可以对同一个容器对象执行accumulator()。并且仅当UNORDERED时,才应该使用这种并发缩减。(为了线程安全,此时的容器应该是线程安全的)
      • 收集器本身是支持compose(组合),例如计算出员工工资总和的一个收集器,可以被通过分组的组合再次使用。

        Collector<Employee, ?, Integer> summingSalaries
        = Collectors.summingInt(Employee::getSalary))

        Collector<Employee, ?, Map<Department, Integer>> summingSalariesByDept
        = Collectors.groupingBy(Employee::getDepartment, summingSalaries);

        static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;
        private final Set<Characteristics> characteristics;

        default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
        }

        Student student1 = new Student("A", 90);
        Student student2 = new Student("B", 80);
        Student student3 = new Student("C", 100);
        Student student4 = new Student("D", 90);
        Student student5 = new Student("D", 70);
        List<Student> list = Arrays.asList(student1, student2, student3, student4, student5);

        Map<String, Student> map2 = list.stream().collect(Collectors.groupingBy(Student::getName,
        Collectors.collectingAndThen(Collectors.minBy(Comparator.comparingInt(Student::getScore)), Optional::get)));
        System.out.println(map2);


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

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

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

        @Override
        public Function<Set<T>, Set<T>> finisher() {
        System.out.println("finisher invoked");
        return set -> set;
        }

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

        }

        • supplier:构造中间结果容器
        • accmulator:将流中的元素累加到中间结果容器中
        • combiner:将fork的中间结果容器join起来
        • finisher:如果中间结果容器和需要返回的结果容器不同,需要finisher内实现转化代码
        • characteristics:定义当前收集器的特性

          • CONCURRENT:中间结果容器线程安全,支持并发写入。如果设置了CONCURRENT,收集器将放弃forkjoin模式,直接让线程操作同一个中间结果容器。

            • 如果容器并不是线程安全的,但是却提供了CONCURRENT属性,如果出现安全性问题(即并发修改),jvm将会抛出ConcurrentModificationException
            • 并且不会执行combiner返回的方法
            • UNSORTED:收集器不保证返回的结果容器中元素的顺序与流中的顺序完全相同。
            • IDENTITY_FINISH:告诉编译器中间结果容器是可以通过强制类型转换安全的变成最终类型(即最终结果容器类型 super 中间结果容器类型),则编译器不再执行finisher方法,直接进行强制类型转换。
            
        
        实现自定义收集器,必须override上面这5个方法:
        
        自定义收集器实现
        --------
        
        源码实现上应该是当comparingInt的返回结果为0时,才会继续调用thenComparing
        
        先根据name排序,如果name相等,则根据分数排序。
        
        thenCoparing可以理解为次级排序
        
        ### thenComparing
        
        默认比较器为升序排序,如果调用reversed,则返回的为倒序排序
        
        ### reversed
        
        Comparator是一个函数式接口,只有一个抽象方法`int compare(T o1, T o2);`,其余提供了很多默认方法。
        
        *   返回-1,则表示o1比o2小。
            
        *   返回 0,则表示o1和o2相等
            
        *   返回1,则表示o1比o2大
            
        
        Comparator是一个比较器,从JDK1.2时候开始提供。其中最重要的方法应该是`int compare(T o1, T o2);`方法。
        
        Comparator
        ----------
        
        Collectors是一个收集器工厂,为开发者提供一些常用的收集器和收集器方法。Collectors的收集器是私有的,所以无法直接实例化这个工厂。
        
        `Collectors`是`Collector`接口的官方唯一实现类。`Collecotrs`维护了一个CollectorImpl的内部类
        
        Collectors
        ----------
        
        *   `CONCURRENT`:表示这个收集器是可以并发的。
            
            *   这里的并发和parallelStream不同,parallStream是通过创建多个结果容器,再通过combiner合并到一起。
                
            *   而这个Concurrent表示,同一个结果容器支持多个线程同时调用。(这个容器需要线程安全的)
                
        *   `UNORDERED`:收集操作并不保证与输入元素顺序一致。
            
        *   `IDENTITY_FINISH`:如果中间的结果容器类型就是返回容器的类型,这时候才可以配置,finsher函数会直接将结果类型转化为传入的参数类型
            
        
        #### Characteristics

伟大的卷发
4 声望4 粉丝

日拱一卒,以求寸进