Java8 Stream(12)Collectors.groupingBy 分组统计详解


工作中能够熟练使用Collectors中groupingBy、reducing、toMap非常重要,因为这些巧妙的函数可以大大提高开发效率,所以学习好它们刻不容缓。先准备好一个List集合供测试用。

public static List<User> getUserList() {
    
    
    List<User> users = new ArrayList<>();
    users.add(new User("1", "name1", "Java组", 33, "男", new BigDecimal("25000"), true));
    users.add(new User("2", "name2", "Java组", 31, "女", new BigDecimal("28000"), true));
    users.add(new User("3", "name3", "前端组", 33, "男", new BigDecimal("18000"), true));
    users.add(new User("4", "name4", "前端组", 25, "男", new BigDecimal("19000"), false));
    users.add(new User("5", "name5", "QA组", 24, "女", new BigDecimal("15000"), true));
    users.add(new User("6", "name6", "产品组", 34, "女", new BigDecimal("12000"), true));
    return users;
}

1 List 转 Map

1.1 使用 groupingBy 分组

根据部门分组

Map<String, List<User>> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept
        )
);

按照自定义Key分组

Map<String, List<User>> groupByDeptAppendName = users.stream().collect(
        Collectors.groupingBy(
                user -> user.getDept() + "&" + user.getName()
        )
);

多级分组

 Map<String, Map<String, List<User>>> groupByDeptAndGender = users.stream()
         .filter(user -> Objects.nonNull(user.getSex())) // group by 的字段不能有null值
         .collect(
                 Collectors.groupingBy(
                         User::getDept,
                         Collectors.groupingBy(User::getSex)
                 )
         );

根据部门分组,求ID的List

Map<String, List<String>> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.mapping(
                        User::getId,
                        Collectors.toList()
                )
        )
);
{
    
    Java组=[1, 2], QA=[5], 前端组=[3, 4], 产品组=[6]}

根据部门分组,Count人数

Map<String, Long> groupBuyDeptThenCount = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.counting()
        )
);

根据部门分组,求Sex的Set

Map<String, Set<String>> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.mapping(
                        User::getSex,
                        Collectors.toSet()
                )
        )
);
{
    
    Java组=[女, 男], QA组=[], 前端组=[], 产品组=[]}

根据部门分组,求Sex的去重个数

Collectors.collectingAndThen() 它接受两个参数:downstreamfinisher。其中

  • downstream是一个Collector收集器,用于对数据流中的元素进行收集操作;
  • finisher是一个Function函数,用于对downstream的收集结果进行处理,并返回最终的结果。
Map<String, Integer> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.mapping(
                                User::getSex,
                                Collectors.toSet()
                        ),
                        a -> a.size()
                )
        )
);
{
    
    Java组=2, QA组=1, 前端组=1, 产品组=1}

1.2 使用 partitioningBy 分区

Map<Boolean, List<User>> collect = users.stream().collect(
        Collectors.partitioningBy(
                a -> a.getAge() > 30
        )
);

1.3 使用 toMap

List 转 Map<ID, User>

Map<String, User> userMap = users.stream().collect(
                Collectors.toMap(
                        User::getId,
                        Function.identity(),
                        (k1, k2) -> k1 //key重复,用第一个
                )
        );

List 转 Map<ID, Name>

Map<String, String> idToName = users.stream().collect(
        Collectors.toMap(
                User::getId,
                User::getName
        )
);

2 求最大值、最小值、平均值、总和

2.1 不分组,直接统计

求年龄最大的人:
Optional<User> maxAgeUserOptional = users.stream().collect(
        Collectors.maxBy(Comparator.comparing(User::getAge))
);

求年龄最小的人:
Optional<User> minAgeUserOptional = users.stream().collect(
        Collectors.minBy(Comparator.comparing(User::getAge))
);

求最大的年龄:
int maxAge = users.stream().mapToInt(User::getAge).max().getAsInt();

求最小的年龄:
int minAge = users.stream().mapToInt(User::getAge).min().getAsInt();

求年龄总和:
int sumAge = users.stream().mapToInt(User::getAge).sum();

求平均年龄:
double avgAge = users.stream().mapToInt(User::getAge).average().getAsDouble();

2.2 先分组,再统计

求各个部门中,Age最大的人

Map<String, User> groupByDeptThenGetMaxAgeUser = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.maxBy(
                                Comparator.comparing(
                                        User::getAge,
                                        Comparator.nullsLast(Integer::compareTo)
                                )
                        ),
                        Optional::get
                )
        )
);

求各个部门中,Age的最大值

Map<String, Integer> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.maxBy(
                                Comparator.comparingInt(User::getAge)
                        ),
                        a -> a.isPresent() ? a.get().getAge() : null
                )

        )
);

求各个部门中,Age的平均值

Map<String, Double> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.averagingInt(User::getAge)

        )
);

求各个部门中,Age的总和

 Map<String, Integer> collect = users.stream().collect(
         Collectors.groupingBy(
                 User::getDept,
                 Collectors.summingInt(User::getAge)
         )
 );

使用 IntSummaryStatistics 统计

Map<String, IntSummaryStatistics> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.summarizingInt(
                        User::getAge
                )
        )
);
for (Map.Entry<String, IntSummaryStatistics> entry : collect.entrySet()) {
    
    
    IntSummaryStatistics summaryStatistics = entry.getValue();
    System.out.println("----------------key----------------" + entry.getKey());
    System.out.println("求和:" + summaryStatistics.getSum());
    System.out.println("求平均" + summaryStatistics.getAverage());
    System.out.println("求最大:" + summaryStatistics.getMax());
    System.out.println("求最小:" + summaryStatistics.getMin());
    System.out.println("求总数:" + summaryStatistics.getCount());
}

3 BigDecimal 类型处理

3.1 不分组,直接统计

List<BigDecimal> userSalary = users.stream().map(User::getSalary).collect(Collectors.toList());

Optional<BigDecimal> maxSalary = userSalary.stream().reduce(BigDecimal::max);
Optional<BigDecimal> minSalary = userSalary.stream().reduce(BigDecimal::min);
BigDecimal sumSalary = userSalary.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal avgSalary= sumSalary.divide(BigDecimal.valueOf(userSalary.size()), 2, BigDecimal.ROUND_HALF_UP);

3.2 先分组,再统计

求各个部门,最大的Salary
Map<String, BigDecimal> groupByDeptThenGetMaxSalary = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.reducing(
                        BigDecimal.ZERO,
                        User::getSalary,
                        BigDecimal::max
                )
        )
);
求各个部门,最小的Salary
Map<String, BigDecimal> groupByDeptThenGetMinSalary = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.reducing(
                        BigDecimal.valueOf(Long.MAX_VALUE),
                        User::getSalary,
                        BigDecimal::min
                )
        )
);

如果考虑Salarynull值,可以如下处理
Map<String, BigDecimal> groupByDeptThenGetMinSalary = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.reducing(
                                (c1, c2) -> c1.getSalary().compareTo(c2.getSalary()) > 0 ? c2 : c1
                        ),
                        a -> a.isPresent() ? a.get().getSalary() : null
                )
        )
);
求各个部门,Salary总和
Map<String, BigDecimal> groupByDeptThenGetSumSalary = users.stream()
        .filter(user -> Objects.nonNull(user.getDept())) //比较的字段不能有null值
        .collect(
                Collectors.groupingBy(
                        User::getDept,
                        Collectors.reducing(
                                BigDecimal.ZERO,
                                User::getSalary,
                                BigDecimal::add
                        )
                )
        );
求各个部门,Salary平均值

Collectors中有averagingInt、averagingLong、averagingDouble等,但是没有averagingBigDecimal
参考java lambada 对list进行分组汇总,实现自定义averagingBigDecimal

Map<String, BigDecimal> groupByDeptThenGetAvgSalary = users.stream()
        .filter(user -> Objects.nonNull(user.getDept())) //比较的字段不能有null值
        .collect(
                Collectors.groupingBy(
                        User::getDept,
                        CustomCollectors.averagingBigDecimal(User::getSalary, 2, 2)
                )
        );

猜你喜欢

转载自blog.csdn.net/winterking3/article/details/131671534
今日推荐