美文网首页
java8-09-自定义Collector-groupBy2

java8-09-自定义Collector-groupBy2

作者: hylexus | 来源:发表于2017-12-31 20:16 被阅读19次

    [TOC]

    声明

    这一系列文章旨在帮助大家理解 Collector 的执行流程,至于实现的是否高效、是否优雅、是否合理等暂且不论。

    若对 Collector 的各个流程有疑问请移驾此处:http://blog.csdn.net/hylexus/article/details/78941843


    年末了,坑爹的本命年也很快结束了,今天写本命年最后一篇博客:

    看着这些名门正派的武功心法(Collectos源码),心底往往会泛起一股淡淡的忧伤,一阶江湖散修的无奈V_V。

    每每瞄到大派优雅的心法秘诀,内心总会在呐喊:“还有这种操作?”。

    不是说看不懂这些大神的实现,只是在没看到人家优雅的实现之前,自己是想不到的V_V。

    上次分享的自定义 groupByCollector 实现了分组的功能,但是分组之后的每个分类下都是原来输入的类型。

    很多时候,你可能只是需要输入类型的某个属性而已。

    比如,分组之后返回的是 Map<Integer, List<User>> ,但是你需要的可能仅仅是user的id,也就是你想要的只是这个: Map<Integer, List<Integer>> ,其中只包含了 user.id

    现在就来试试实现这个功能,当然此处的实现还是很死板的。很多东西都是写死的。不支持像jdk原生分组功能那样灵活的实现和组合。

    简单实现

    • 此处还是拿上一篇中的例子来说
    static class User {
        private Integer id;
        private String name;
        private Integer gender;
        private Integer age;
    
        User(Integer id, String name, Integer gender, Integer age) {
            this.id = id;
            this.name = name;
            this.gender = gender;
            this.age = age;
        }
        // getter,setter
    }
    
    List<User> users = Lists.newArrayList(
            new User(1, "java", 1, 25),
            new User(2, "C", 1, 22),
            new User(3, "scala", 0, 23),
            new User(4, "C++", 0, 11),
            new User(5, "Spark", 1, 25),
            new User(6, "PHP", 0, 45),
            new User(7, "Python", 1, 89),
            new User(8, "JavaScript", 0, 110),
            new User(9, "C#", 1, 33)
    );
    
    • GroupByCollector
    /**
     * @param <K> 分组之后的key
     * @param <T> 中间转换过程的元素类型
     * @param <R> 最终的结果类型
     * @author hylexus
     */
    static class GroupByCollector3<K, T, R> 
      implements Collector<T, Map<K, List<T>>, Map<K, List<R>>> {
    
        /**
         * 分类器,指定分组之后的key
         */
        Function<T, K> classifier;
        /**
         * 最后一步的转换函数
         */
        Function<T, R> finisher;
    
        public GroupByCollector3(Function<T, K> classifier, Function<T, R> finisher) {
            this.classifier = classifier;
            this.finisher = finisher;
        }
    
        @Override
        public Supplier<Map<K, List<T>>> supplier() {
            return HashMap::new;
        }
    
        @Override
        public BiConsumer<Map<K, List<T>>, T> accumulator() {
            return (map, e) -> {
                K key = classifier.apply(e);
                List<T> val = Optional.ofNullable(map)
                  .map(m -> m.get(key))
                  .orElse(Lists.newArrayList());
                val.add(e);
                map.put(key, val);
            };
        }
    
        @Override
        public BinaryOperator<Map<K, List<T>>> combiner() {
            return (m1, m2) -> {
                m2.forEach((k, v) -> {
                    List<T> val = Optional.ofNullable(m1)
                      .map(m -> m.get(k))
                      .orElse(Lists.newArrayList());
                    val.addAll(v);
                });
                return m1;
            };
        }
    
        @Override
        public Function<Map<K, List<T>>, Map<K, List<R>>> finisher() {
            Function<Map<K, List<T>>, Map<K, List<R>>> function = map -> {
                Map<K, List<R>> ret = new HashMap<>();
    
                map.entrySet().stream().forEach(e -> {
                    List<R> v = e.getValue().stream().map(finisher)
                            .collect(Collectors.toList());
                    ret.put(e.getKey(), v);
                });
                return ret;
            };
            return function;
        }
    
        @Override
        public Set<Characteristics> characteristics() {
            return Collections.emptySet();
        }
    }
    

    使用自定义Collector

    • 自定义Collector,是获取分组中的userId而非user实例
    Map<Integer, List<Integer>> collect = users.stream()
            .collect(new GroupByCollector3<>(User::getGender, User::getId));
    collect.forEach((k, v) -> {
        System.out.println(k);
        System.out.println("\t" + v);
    });
    
    • 当然,使用jdk内置的实现起来更优雅
    Map<Integer, List<Integer>> collect = users.stream()
            .collect(
                    Collectors.groupingBy(
                            User::getGender,
                            Collectors.mapping(User::getId, Collectors.toList())
                    )
            );
    collect.forEach((k, v) -> {
        System.out.println(k);
        System.out.println("\t" + v);
    });
    
    • 输出效果
    0
        [3, 4, 6, 8]
    1
        [1, 2, 5, 7, 9]
    

    博客源码

    https://github.com/hylexus/blog-src/blob/master/java-core/src/test/java/cn/hylexus/lambda/CollectorTest.java

    相关文章

      网友评论

          本文标题:java8-09-自定义Collector-groupBy2

          本文链接:https://www.haomeiwen.com/subject/gbbagxtx.html