java stream 使用指南-------groupingBy进阶用法

前言

刚开始我只会使用普通的for、while循环,最多就增强for循环,后来在项目上看到其他同事在用,看起来很新颖、简洁的样子,然后我也开始尝试使用,用顺手之后发现是真的香。在使用的过程中,有些时候,不能完全实现自己所想的,也没少百度,所以呢,就想着把这些稍微高级一点、难一点的用法记录下来,

数据准备:

public class BlogMain1 {

    @Data
    @AllArgsConstructor
    static class User{
        private String username;
        private String gender;
        private String group;
        private Integer age;
    }

    @Data
    @AllArgsConstructor
    static class Group {
        private String name;
        private String desc;
    }

    private List<User> users = new ArrayList<User>(){{
        add(new User("张无忌","男","明教",23));
        add(new User("金毛狮王--谢逊","男","明教",47));
        add(new User("紫衫龙王--黛绮丝","女","明教",32));
        add(new User("白眉鹰王--殷天正","男","明教",38));
        add(new User("青翼蝠王--韦一笑","男","明教",35));
        add(new User("张三丰","男","武当",56));
        add(new User("宋远桥","男","武当",42));
        add(new User("张翠山","男","武当",41));
        add(new User("殷梨亭","男","武当",37));
        add(new User("灭绝师太","女","峨眉",51));
        add(new User("周芷若","女","峨眉",21));
        add(new User("宋青书","男","峨眉",22));
        add(new User("圆真","男","少林",35));
        add(new User("空智","男","少林",47));
    }};
}

一. 分组 groupingBy

基础用法

1. 按照某个属性分组

代码

 // key是门派名称,value是门派里面所有的人
 Map<String, List<User>> collect = users.stream().collect(Collectors.groupingBy(User::getGroup));

结果

{武当=[BlogMain1.User(username=张三丰, gender=, group=武当, age=56), BlogMain1.User(username=宋远桥, gender=, group=武当, age=42), BlogMain1.User(username=张翠山, gender=, group=武当, age=41), BlogMain1.User(username=殷梨亭, gender=, group=武当, age=37)], 明教=[BlogMain1.User(username=张无忌, gender=, group=明教, age=23), BlogMain1.User(username=金毛狮王--谢逊, gender=, group=明教, age=47), BlogMain1.User(username=紫衫龙王--黛绮丝, gender=, group=明教, age=32), BlogMain1.User(username=白眉鹰王--殷天正, gender=, group=明教, age=38), BlogMain1.User(username=青翼蝠王--韦一笑, gender=, group=明教, age=35)], 峨眉=[BlogMain1.User(username=灭绝师太, gender=, group=峨眉, age=51), BlogMain1.User(username=周芷若, gender=, group=峨眉, age=21), BlogMain1.User(username=宋青书, gender=, group=峨眉, age=22)], 少林=[BlogMain1.User(username=圆真, gender=, group=少林, age=35), BlogMain1.User(username=空智, gender=, group=少林, age=47)]}
2. 分组统计
// key是门派名称,value是门派人数
Map<String, Long> collect1 = users.stream().collect(Collectors.groupingBy(User::getGroup, 
        Collectors.counting()));
// {武当=4, 明教=5, 峨眉=3, 少林=2}
3. 分组求和
// key是门派名称,value是门派人年龄之和
Map<String, Long> collect2 = users.stream().collect(Collectors.groupingBy(User::getGroup, 
        Collectors.summingLong(User::getAge)));
// {武当=176, 明教=175, 峨眉=94, 少林=82}
4. 自定义分组条件------按姓名长度分组

代码

// 按照姓名长度分组
Map<Integer, List<User>> collect3 = users.stream().collect(Collectors.groupingBy(x -> x.username.length()));

结果

{2=[BlogMain1.User(username=圆真, gender=, group=少林, age=35), BlogMain1.User(username=空智, gender=, group=少林, age=47)], 3=[BlogMain1.User(username=张无忌, gender=, group=明教, age=23), BlogMain1.User(username=张三丰, gender=, group=武当, age=56), BlogMain1.User(username=宋远桥, gender=, group=武当, age=42), BlogMain1.User(username=张翠山, gender=, group=武当, age=41), BlogMain1.User(username=殷梨亭, gender=, group=武当, age=37), BlogMain1.User(username=周芷若, gender=, group=峨眉, age=21), BlogMain1.User(username=宋青书, gender=, group=峨眉, age=22)], 4=[BlogMain1.User(username=灭绝师太, gender=, group=峨眉, age=51)], 8=[BlogMain1.User(username=金毛狮王--谢逊, gender=, group=明教, age=47)], 9=[BlogMain1.User(username=紫衫龙王--黛绮丝, gender=, group=明教, age=32), BlogMain1.User(username=白眉鹰王--殷天正, gender=, group=明教, age=38), BlogMain1.User(username=青翼蝠王--韦一笑, gender=, group=明教, age=35)]}
5. 自定义分组条件------按照多个字段分组

代码

// 多个属性分组,按照门派和性别分组
Map<String, List<User>> collect4 = users.stream().collect(Collectors.groupingBy(x -> x.getGroup() + "_" + x.getGender()));

结果

{少林_男=[BlogMain1.User(username=圆真, gender=, group=少林, age=35), BlogMain1.User(username=空智, gender=, group=少林, age=47)], 明教_男=[BlogMain1.User(username=张无忌, gender=, group=明教, age=23), BlogMain1.User(username=金毛狮王--谢逊, gender=, group=明教, age=47), BlogMain1.User(username=白眉鹰王--殷天正, gender=, group=明教, age=38), BlogMain1.User(username=青翼蝠王--韦一笑, gender=, group=明教, age=35)], 峨眉_男=[BlogMain1.User(username=宋青书, gender=, group=峨眉, age=22)], 武当_男=[BlogMain1.User(username=张三丰, gender=, group=武当, age=56), BlogMain1.User(username=宋远桥, gender=, group=武当, age=42), BlogMain1.User(username=张翠山, gender=, group=武当, age=41), BlogMain1.User(username=殷梨亭, gender=, group=武当, age=37)], 明教_女=[BlogMain1.User(username=紫衫龙王--黛绮丝, gender=, group=明教, age=32)], 峨眉_女=[BlogMain1.User(username=灭绝师太, gender=, group=峨眉, age=51), BlogMain1.User(username=周芷若, gender=, group=峨眉, age=21)]}

高级用法

只是个人感觉比上面的高级一些,可能在大佬看来本质上都一样,只是更复杂了一点点。

1. 改变分组后的key和value

在上面的分组中,key还好,在4和5自定义条件分组中,改变了key。但是value呢?好像都是定死了,普通的分组就是该对象的集合,求和、统计就是一个数字,能不能我自己来规定返回的value类型呢?比如我要按照门派分组,得到每个门派里面每个人的名字,不需要其他的性别、年龄信息

代码

// 按照门派分组,分组后 key 为Group对象, value变成String名字
Map<Group, List<String>> collect2 = users.stream()
                .collect(Collectors.groupingBy(x -> new Group(x.getGroup(), x.getGroup() + "的描述"),
                        Collectors.mapping(User::getUsername, Collectors.toCollection(ArrayList::new))));

结果

{BlogMain1.Group(name=明教, desc=明教的描述)=[张无忌, 金毛狮王--谢逊, 紫衫龙王--黛绮丝, 白眉鹰王--殷天正, 青翼蝠王--韦一笑], BlogMain1.Group(name=武当, desc=武当的描述)=[张三丰, 宋远桥, 张翠山, 殷梨亭], BlogMain1.Group(name=少林, desc=少林的描述)=[圆真, 空智], BlogMain1.Group(name=峨眉, desc=峨眉的描述)=[灭绝师太, 周芷若, 宋青书]}
2. 多级分组

先按照一个条件分组,在分组的基础上再按照另一个条件分组

代码

// 多级分组,先按照门派分组,门派里面再按照名字长度分组
Map<Group, Map<Integer, List<User>>> collect3 = users.stream().collect(
        Collectors.groupingBy(x -> new Group(x.getGroup(), x.getGroup() + "的描述"),
                Collectors.groupingBy(x -> x.username.length())));

结果

{BlogMain1.Group(name=明教, desc=明教的描述)={3=[BlogMain1.User(username=张无忌, gender=, group=明教, age=23)], 8=[BlogMain1.User(username=金毛狮王--谢逊, gender=, group=明教, age=47)], 9=[BlogMain1.User(username=紫衫龙王--黛绮丝, gender=, group=明教, age=32), BlogMain1.User(username=白眉鹰王--殷天正, gender=, group=明教, age=38), BlogMain1.User(username=青翼蝠王--韦一笑, gender=, group=明教, age=35)]}, BlogMain1.Group(name=武当, desc=武当的描述)={3=[BlogMain1.User(username=张三丰, gender=, group=武当, age=56), BlogMain1.User(username=宋远桥, gender=, group=武当, age=42), BlogMain1.User(username=张翠山, gender=, group=武当, age=41), BlogMain1.User(username=殷梨亭, gender=, group=武当, age=37)]}, BlogMain1.Group(name=少林, desc=少林的描述)={2=[BlogMain1.User(username=圆真, gender=, group=少林, age=35), BlogMain1.User(username=空智, gender=, group=少林, age=47)]}, BlogMain1.Group(name=峨眉, desc=峨眉的描述)={3=[BlogMain1.User(username=周芷若, gender=, group=峨眉, age=21), BlogMain1.User(username=宋青书, gender=, group=峨眉, age=22)], 4=[BlogMain1.User(username=灭绝师太, gender=, group=峨眉, age=51)]}}
3. 分组排序

就是先分组, 再每组分别排序, 经常写SQL的小伙伴可能经常碰到这个问题, steam api 就非常简单

代码

Map<String, List<User>> userMap = users.stream().collect(Collectors.groupingBy(User::getGender,
        Collectors.mapping(u -> u, Collectors.collectingAndThen(Collectors.toList(),
                sortedStudents -> sortedStudents.stream().sorted((Comparator.comparing(User::getAge)))
                        .collect(Collectors.toList())))));

userMap.forEach((k, v) -> {
    System.err.println("k: " + k);
    v.forEach(System.err::println);
});

结果

k:BlogMain1.User(username=周芷若, gender=, group=峨眉, age=21)
BlogMain1.User(username=紫衫龙王--黛绮丝, gender=, group=明教, age=32)
BlogMain1.User(username=灭绝师太, gender=, group=峨眉, age=51)
k:BlogMain1.User(username=宋青书, gender=, group=峨眉, age=22)
BlogMain1.User(username=张无忌, gender=, group=明教, age=23)
BlogMain1.User(username=青翼蝠王--韦一笑, gender=, group=明教, age=35)
BlogMain1.User(username=圆真, gender=, group=少林, age=35)
BlogMain1.User(username=殷梨亭, gender=, group=武当, age=37)
BlogMain1.User(username=白眉鹰王--殷天正, gender=, group=明教, age=38)
BlogMain1.User(username=张翠山, gender=, group=武当, age=41)
BlogMain1.User(username=宋远桥, gender=, group=武当, age=42)
BlogMain1.User(username=金毛狮王--谢逊, gender=, group=明教, age=47)
BlogMain1.User(username=空智, gender=, group=少林, age=47)
BlogMain1.User(username=张三丰, gender=, group=武当, age=56)
4. 我在项目中的使用例子

在这里插入图片描述

暂时就想到这么多,欢迎补充

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值