[TOC]

声明

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

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


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

看着这些名门正派的武功心法(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/bl...


诸葛流云
261 声望17 粉丝

引用和评论

0 条评论