zoukankan      html  css  js  c++  java
  • Java Collectors API实践

    概述

    Collectors是一个公共final 类,它扩展了Object 类。Collectors 类提供了各种有用的归约操作,例如将元素累积到集合中,根据各种标准汇总元素等。PS:Collectors 类中的所有方法都是静态的。所以最好使用静态导入,一般IDE会替我们做这个事情,无需多关心。

    准备

    我们将在本文中使用以下FunTester类。

        private static class FunTester {
    
            public int id;
    
            public String name;
    
            public int age;
    
            public String title;
    
            public double size;
    
            public FunTester(int id, String name, int age, String title, double size) {
                this.id = id;
                this.name = name;
                this.age = age;
                this.title = title;
                this.size = size;
            }
        此处省略的getter和setter以及tostring方法
        }
    

    然后我们创建几个FunTester对象。

            List<FunTester> funs = new ArrayList<>();
            List<FunTester> funs = new ArrayList<>();
            funs.add(new FunTester(1, "1号", 10, "机器人", 1.1));
            funs.add(new FunTester(2, "2号", 20, "机器人", 2.2));
            funs.add(new FunTester(3, "3号", 30, "背锅侠", 3.3));
            funs.add(new FunTester(4, "4号", 40, "BUG君", 4.4));
            funs.add(new FunTester(5, "5号", 50, "菜鸟", 5.5));
    

    Stream.collect()示例

    Java 8 最强大的stream方法是collect()方法。这是Stream API 中非常重要的的一部分。它允许我们对 Stream 实例中保存的数据元素执行可变折叠操作(将元素重新打包为某些数据结构并应用一些额外的逻辑、连接它们等)。这些逻辑的实现都是通过 Collector 接口实现提供的,源码 <R, A> R collect(Collector<? super T, A, R> collector);

    Collectors.toList()示例

    toList() 收集器可用于将所有 Stream 元素收集到一个 List 实例中。

    下面展示将所有FunTester对象的名字收集到一个list当中。

            List<String> names = funs.stream().map(f -> f.name).collect(Collectors.toList());
            output(names);
    

    控制台输出:

    INFO-> main 第1个:1号
    INFO-> main 第2个:2号
    INFO-> main 第3个:3号
    INFO-> main 第4个:4号
    INFO-> main 第5个:5号
    

    Collectors.toSet() 示例

    toSet() 收集器用于将所有 Stream 元素收集到一个 Set 实例中,与toList()区别在于会去重。

    下面我展示收集所有不重复的title信息。

            Set<String> titles = funs.stream().map(f -> f.title).collect(Collectors.toSet());
            output(titles);
    

    控制台输出:

    INFO-> main 机器人
    INFO-> main 背锅侠
    INFO-> main 菜鸟
    INFO-> main BUG君
    

    PS:这里的set存储的内容是无序的。

    Collectors.toCollection() 示例

    在使用 toSet() 和 toList() 收集器时,无法对收集结果进行设置和处理。如果要使用自定义实现或 LinkedList 或 TreeSet,则需要使用toCollection 收集器。

    例如将name收集到 LinkedList 的示例,而不是默认的 List 实现。

            LinkedList<String> names = funs.stream().map(f -> f.name).collect(Collectors.toCollection(LinkedList::new));
    

    Collectors.toMap() 示例

    toMap()API是将收集结果转成map,看源码略微复杂了,其中有两个3个重载方法,我把参数最多的展示一下:

        /**
         * @param <T> the type of the input elements
         * @param <K> the output type of the key mapping function
         * @param <U> the output type of the value mapping function
         * @param <M> the type of the resulting {@code Map}
         * @param keyMapper a mapping function to produce keys
         * @param valueMapper a mapping function to produce values
         * @param mergeFunction a merge function, used to resolve collisions between
         *                      values associated with the same key, as supplied
         *                      to {@link Map#merge(Object, Object, BiFunction)}
         * @param mapSupplier a function which returns a new, empty {@code Map} into
         *                    which the results will be inserted
         * @return a {@code Collector} which collects elements into a {@code Map}
         */
        public static <T, K, U, M extends Map<K, U>>
        Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction,
                                    Supplier<M> mapSupplier) {
            BiConsumer<M, T> accumulator
                    = (map, element) -> map.merge(keyMapper.apply(element),
                                                  valueMapper.apply(element), mergeFunction);
            return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
        }
    
    

    其中四个参数都是闭包,这个在之前讲过Java、Groovy、Python和Golang如何把方法当作参数这里不多说语法了。第一个参数是设置key,第二个参数设置value,第三个参数设置的是当key出现重复时候的value,第四个参数是设置返回map的类型。

    下面是我的示例:

            HashMap<Integer, String> collect = funs.stream().collect(Collectors.toMap(new Function<FunTester, Integer>() {
                @Override
                public Integer apply(FunTester funTester) {
                    return funTester.id;
                }
            }, new Function<FunTester, String>() {
                @Override
                public String apply(FunTester funTester) {
                    return funTester.name;
                }
            }, (v1, v2) -> v2, HashMap<Integer, String>::new));
    
    

    简化版如下:

            HashMap<Integer, String> collect = funs.stream().collect(Collectors.toMap(FunTester::getId, FunTester::getName, (v1, v2) -> v2, HashMap<Integer, String>::new));
    
    

    控制台输出:

    INFO-> main {1=1号, 2=2号, 3=3号, 4=4号, 5=5号}
    

    Collectors.summingInt() 示例

    summingInt()方法对从流中提取的所有元素求和并返回 Integer值,下面是求所有FunTester对象年龄统计结果的演示:

            IntSummaryStatistics statistics = funs.stream().map(FunTester::getAge).collect(Collectors.summarizingInt(f -> f));
            output(statistics.getSum());
            output(statistics.getAverage());
            output(statistics.getCount());
            output(statistics.getMax());
            output(statistics.getMin());
    

    控制台输出:

    INFO-> main 150
    INFO-> main 30.0
    INFO-> main 5
    INFO-> main 50
    INFO-> main 10
    
    

    同样还有两个类似的API:java.util.stream.Collectors#summarizingLongjava.util.stream.Collectors#summarizingDouble

    此外java.util.stream.Collectors还提供了快捷的求平均数的三个方法:

            funs.stream().map(FunTester::getAge).collect(Collectors.averagingDouble(f -> f));
            funs.stream().map(FunTester::getAge).collect(Collectors.averagingInt(f -> f));
            funs.stream().map(FunTester::getAge).collect(Collectors.averagingLong(f -> f));
    

    还有一个求数量的API:

            funs.stream().map(FunTester::getAge).collect(Collectors.counting());
    
    

    Collectors.joining() 示例

    Collectors.joining()功能是将stream里面的数据转成String类型,这个方法也有三个重载,下面展示一下最多参数的。

        /**
         * Returns a {@code Collector} that concatenates the input elements,
         * separated by the specified delimiter, with the specified prefix and
         * suffix, in encounter order.
         *
         * @param delimiter the delimiter to be used between each element
         * @param  prefix the sequence of characters to be used at the beginning
         *                of the joined result
         * @param  suffix the sequence of characters to be used at the end
         *                of the joined result
         * @return A {@code Collector} which concatenates CharSequence elements,
         * separated by the specified delimiter, in encounter order
         */
        public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                                 CharSequence prefix,
                                                                 CharSequence suffix) {
            return new CollectorImpl<>(
                    () -> new StringJoiner(delimiter, prefix, suffix),
                    StringJoiner::add, StringJoiner::merge,
                    StringJoiner::toString, CH_NOID);
        }
    

    其中三个参数分别是,前缀,后缀和分隔符。

    下面演示一下效果:

            String join = funs.stream().map(FunTester::getTitle).collect(Collectors.joining(",","[","]"));
    

    控制台输出:

    INFO-> main [机器人,机器人,背锅侠,BUG君,菜鸟]
    

    Collectors.groupingBy() 示例

    Collectors.groupingBy()是将stream数据进行分组的API,参数也有一个java.util.function.Function,这里展示一下对对象name按照长度进行分组:

            Map<Integer, List<String>> group = funs.stream().map(FunTester::getTitle).collect(Collectors.groupingBy(String::length));
            output(group);
    

    控制台输出:

    INFO-> main {2=[菜鸟], 3=[机器人, 机器人, 背锅侠], 4=[BUG君]}
    

    PS:这里Java里面::语法不支持传递,只能使用一次。

    Collectors.partitioningBy() 示例

    Collectors.partitioningBy()这个是根据判断条件分组,判断条件就是传入的参数。下面我演示一下:

            Map<Boolean, List<FunTester>> group = funs.stream().collect(Collectors.partitioningBy(f -> f.getId() > 2));
            output(group.get(false));
            output("----------------");
            output(group.get(true));
    

    控制台输出:

    INFO-> main 第1个:FunTester{id=1, name='1号', age=10, title='机器人', size=1.1}
    INFO-> main 第2个:FunTester{id=2, name='2号', age=20, title='机器人', size=2.2}
    INFO-> main ----------------
    INFO-> main 第1个:FunTester{id=3, name='3号', age=30, title='背锅侠', size=3.3}
    INFO-> main 第2个:FunTester{id=4, name='4号', age=40, title='BUG君', size=4.4}
    INFO-> main 第3个:FunTester{id=5, name='5号', age=50, title='菜鸟', size=5.5}
    

    Collectors.toConcurrentMap() 示例

    这个跟tomap()是一样的,语法都通用,可以参考上面的演示。

    欢迎关注FunTester,Have Fun ~ Tester !

  • 相关阅读:
    按位与、或、非、异或总结
    Linux 挂载命令
    Linux 文件系统常用命令
    Linux 系统命令sudo权限
    Linux 文件系统属性chattr权限
    Linux 文件特殊权限-Sticky BIT
    Linux 文件特殊权限-SetGID
    Linux 文件特殊权限-SetUID
    Linux 递归acl权限和默认acl权限
    Linux 最大有效权限与删除ACL
  • 原文地址:https://www.cnblogs.com/FunTester/p/15600043.html
Copyright © 2011-2022 走看看