造一个需求,有一批数据,需要某个字段进行分组,分组后对每组的值求平均(当然可以是任何操作)。

为了便于说明,用Json描述数据结构。

[    
    {
        "name": "John",
        "sex": "male",
        "age": 34
    },
    {
        "name": "Tom",
        "sex": "male",
        "age": 27
    },    {
        "name": "Simon",
        "sex": "female",
        "age": 33
    },
    {
        "name": "Nancy",
        "sex": "female",
        "age": 36
    },
    {
        "name": "Jackson",
        "sex": "male",
        "age": 44
    },
    {
        "name": "Jone",
        "sex": "male",
        "age": 25
    },
    {
        "name": "Rolin",
        "sex": "female",
        "age": 41
    }
]

对于上面的数据,首先根据sex进行分组,然后求出男女平均年龄。

{
    "mele": 33,
    "female": 31
}

用到的实体类和工具类,对于getter/setter方法就省略了。

class User {
    private String name;
    private String sex;
    private Integer age;
}

final class UserUtils {
    public static Integer(List<User> users) {
        int count = 0;
        for (User user: users) {
            count += user.getAge();
        }
        return users.isEmpty() ? 0 : count / users.size(); 
    }
}

处理方式如下。

users.stream()
    .collect(Collectors.groupingBy(User::getSex))
    .entrySet()
    .stream()
    .map(UserUtils::avgSex)
    .map(Map::entrySet)
    .flatMap(Set::stream)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

上面这段刚开始写的时候,对于Stream不是很熟练,导致绕了路,改写如下。

users.stream()
    .collect(Collectors.groupingBy(User::getSex))
    .entrySet()
    .stream()
    .collect(Collectors.toMap(Map.Entry::getKey, UserUtils::avgSex));

另外此写法,如果遇到Key相同便会抛出异常。可以再添加一个合并Function。

users.stream()
    .collect(Collectors.groupingBy(User::getSex))
    .entrySet()
    .stream()
    .collect(Collectors.toMap(Map.Entry::getKey, UserUtils::avgSex), (a ,b) -> a);

这段代码的意思是,如果遇到Key相同的,保留原来的。
如果想用新的替代旧的,可以改写为(a, b) -> b

尽管Java已经到了17,但是8的很多特性也没有被发掘完。也难怪大家升级JDK的欲望如此低。

最后修改:2022 年 07 月 21 日
如果觉得我的文章对你有用,请随意赞赏