java8-09-自定义Collector-groupBy2

[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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271