java流stream中的Collectors中groupingBy源码笔记

Wesley13
• 阅读 634
    /**
     * 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;
        };
    }
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
添砖java的啾 添砖java的啾
3年前
distinct效率更高还是group by效率更高?
目录00结论01distinct的使用02groupby的使用03distinct和groupby原理04推荐groupby的原因00结论先说大致的结论(完整结论在文末):在语义相同,有索引的情况下groupby和distinct都能使用索引,效率相同。在语义相同,无索引的情况下:distinct效率高于groupby。原因是di
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java8新特性
Stream将List转换为Map,使用Collectors.toMap方法进行转换背景:User类,类中分别有id,name,age三个属性。List集合,userList,存储User对象1、指定keyvalue,value是对象中的某个属性值。 Map<Integer,StringuserMap1userList.str
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Wesley13 Wesley13
3年前
Java学习:Stream流式思想
Stream流Java8API添加了一种新的机制——Stream(流)。Stream和IO流不是一回事。流式思想:像生产流水线一样,一个操作接一个操作。使用Stream流的步骤:数据源→转换成流→操作1→操作2→……数据源(source):可以是集合、数组等。St
Wesley13 Wesley13
3年前
Java8 Stream分组
//根据排课id分组Map<Integer,List<Schedule4HomeworkidSchedule4HomeworksMapschedule4Homeworks.stream().collect(Collectors.groupingBy(Schedule4Homework::getScheduleId));
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
Java 8 – Filter a null value from a Stream
Java8–FilteranullvaluefromaStreampackagecom.mkyong.java8;importjava.util.List;importjava.util.stream.Collectors;importjava.util.stream.Stream;publiccla
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这