java流stream中的Collectors中groupingBy源碼筆記


    /**
     * Returns a {@code Collector} implementing a cascaded "group by" operation
     * on input elements of type {@code T}, grouping elements according to a
     * classification function, and then performing a reduction operation on
     * the values associated with a given key using the specified downstream
     * {@code Collector}.  The {@code Map} produced by the Collector is created
     * with the supplied factory function.
     返回一個可級聯的實現了分組功能的收集器,這個收集器根據T類型的輸入參數,通過分類函數進行分類,然后使用指定的下游收集器執行一個匯聚操作。這個收集器的結果容器Map,由給定的工廠方法提供。
     *
     * <p>The classification function maps elements to some key type {@code K}.
     * The downstream collector operates on elements of type {@code T} and
     * produces a result of type {@code D}. The resulting collector produces a
     * {@code Map<K, D>}.
     分類方法將T類型的輸入元素映射為K類型的key,作為結果map的key,下游收集器將T類型的輸入元素轉化為D類型的結果元素,最終結果收集器生產出Map<K,D>的結果。
     *
     * <p>For example, to compute the set of last names of people in each city,
     * where the city names are sorted:
     例子,收集人民群眾的姓氏,結果根據城市分組。
     * <pre>{@code
     * Map<City, Set<String>> namesByCity
     *   = people.stream().collect(
     *     groupingBy(Person::getCity,對應分類函數classifier
     *                TreeMap::new,對應結果容器工廠mapFactory
     *                mapping(Person::getLastName, 對應下游收集器downstream
     *                        toSet())));
     * }</pre>
     *groupingBy對應結果收集器,是最終的收集器。
     * @implNote
     * The returned {@code Collector} is not concurrent.  For parallel stream
     * pipelines, the {@code combiner} function operates by merging the keys
     * from one map into another, which can be an expensive operation.  If
     * preservation of the order in which elements are presented to the downstream
     * collector is not required, using {@link #groupingByConcurrent(Function, Supplier, Collector)}
     * may offer better parallel performance.
     返回的收集器不是並發的,對於並發流來說,組合器合並map的操作可能會很耗性能。
     如果不需要保持元素在流中的順序,推薦使用groupingByConcurrent,這可能要比使用parallel stream的性能更好。
     
     * @param <T> the type of the input elements T:輸入元素的類型
     * @param <K> the type of the keys K:結果map中的key類型。
     * @param <A> the intermediate accumulation type of the downstream collector
     * @param <D> the result type of the downstream reduction
     * @param <M> the type of the resulting {@code Map}
     * @param classifier a classifier function mapping input elements to keys
     * @param downstream a {@code Collector} implementing the downstream reduction
     * @param mapFactory a supplier providing a new empty {@code Map}
     *                   into which the results will be inserted
     * @return a {@code Collector} implementing the cascaded group-by operation
     *
     * @see #groupingBy(Function, Collector)
     * @see #groupingBy(Function)
     * @see #groupingByConcurrent(Function, Supplier, Collector)
     T:輸入元素的類型。
     K:結果map中的key類型。
     A: 下游收集器的累加器的容器類型(累加器的第一個參數)。
     D: 下游收集器的結果類型。當下游收集器沒有finisher的時候,A和D是直接相等的。A強轉為D。
     M: 最終結果類型,即Map<K,D>
     最后返回一個實現了可級聯分組的收集器。
     
     這個方法總體來講,就是給一個分組器,一個最終類型的生產者,一個收集器,根據這三個參數,來改造出一個能分組的收集器。
     */
    public static <T, K, D, A, M extends Map<K, D>> //注意這里有5個參數類型
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream) {
        Supplier<A> downstreamSupplier = downstream.supplier();
        BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
        BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
            // 根據分類器得到的值,最為最終map中的鍵
            K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
            // 得到一下游收集器的生產者生產的容器,最為最終map中的值。
            A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
            // 消費這兩個參數,進行累加操作,相當於修改了下游收集器的收集過程,讓其成為最終收集器的累加器,累積出最終收集器需要的中間結果。
            downstreamAccumulator.accept(container, t);
        };
        // 傳入下游收集器的合並器,得到一個新的合並器,合並器合出來的值是經過改造的累加器的結果,所以是合出的最終類型Map<K,A>
        BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());
        // 將Map<K, D>類型的mapFactory強轉為Map<K, A>類型,這其中包含了A到D的強轉。
        @SuppressWarnings("unchecked")
        Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;
        // 如果集合特性包含IDENTITY_FINISH,說明下游收集器的中間結果就是最終結果,不用再處理finisher
        if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
            return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
        } else {
            //將下游收集器的finisher強轉為輸入A,輸出A的finisher(限定類型)
            @SuppressWarnings("unchecked")
            Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
            // 用強轉好的finisher,處理所有元素,這時的元素是一個一個的map(前面合並的)
            // intermediate代表一個map,將每個map都用改過的finisher處理一下,得到Map<K,A>類型,再強轉一下,將Map<K,A>強轉為Map<K,D>
            Function<Map<K, A>, M> finisher = intermediate -> {
                // 這里replace的只是value,將value處理成A類型
                intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
                @SuppressWarnings("unchecked")
                M castResult = (M) intermediate;
                return castResult;
            };
            return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
        }
    }


    /**
     * {@code BinaryOperator<Map>} that merges the contents of its right
     * argument into its left argument, using the provided merge function to
     * handle duplicate keys.
     將右邊的參數合並到左邊
     *
     * @param <K> type of the map keys
     * @param <V> type of the map values
     * @param <M> type of the map
     * @param mergeFunction A merge function suitable for
     * {@link Map#merge(Object, Object, BiFunction) Map.merge()}
     * @return a merge function for two maps
     */
    private static <K, V, M extends Map<K,V>>
    BinaryOperator<M> mapMerger(BinaryOperator<V> mergeFunction) {
        return (m1, m2) -> {
            for (Map.Entry<K,V> e : m2.entrySet())
                // 如果左邊的map里面,左邊map中沒有右邊合過來的key對應的值,就用右邊合過來的值,
                // ,如果有值,就使用合並器算出來的值,確保不沖突。
                m1.merge(e.getKey(), e.getValue(), mergeFunction);
            return m1;
        };
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM