JDK8特性深刻學習筆記-收集器和比較器(5)

Collector

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

  • collect是stream中的收集器
  • Collector是collect方法中的參數,Collector是一個接口
  • collect是一個可變的匯聚操做,將輸入元素累積到一個可變的結果容器中。
  • Collector當全部的元素都處理完畢後,將累積的結果轉化爲一個最終的表示(這是一個可選操做)
  • Collector同時支持串行和並行操做
  • Collectors自己提供了Collector的常見匯聚實現,Collectors自己其實是一個工廠。
  • Collector是由四個功能指定一塊兒工做以累加條目到一個可變的結果容器,和任選地執行該結果的最終變換:併發

    • 建立一個新的結果容器: supplier()app

      • 若是是並行流,則會在每一個分區都建立一個結果容器。
    • 結合有新的數據元素到結果容器: accumulator()jvm

      • 每一個元素只會調用accumulator()一次
    • 兩個結果的容器組合成一個: combiner()ide

      • Combiner是由BinaryOpreator實現,只用在並行流狀況下使用(串行流只會有一個結果,無需combine)
      • combiner的邏輯:假設有1,2,3,4號線程處理,返回了4個結果,有可能出現的組合方式是:函數

        • 1,2合併,返回5
        • 1,2合併,返回1,1中的元素addAll了2
    • 執行任選的最終容器上變: finisher()
    • Characteristics是一個枚舉類,來判斷結果集合容器是否支持並行操做。
    • 爲了保證併發安全,保證並行和串行執行的結果一致,收集器函數必須知足兩個條件this

      • 同一性(identity):線程

        • 假設a爲其中一個部分累積的結果,那麼a必須知足:a == combiner.apply(a, supplier.get())
        • 結合性(associativity):分割計算必須獲得一個等價的結果code

          • 若是是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
相關文章
相關標籤/搜索