Java中Lambda表达式通过Collectors.groupingBy()配合下游收集器实现分组聚合,支持计数、求平均值、求和、极值及多指标统计(summarizingXXX),并可用collectingAndThen定制处理,需注意空值与返回类型匹配。

Java 中 Lambda 表达式在集合分组后做组内聚合,核心是 Collectors.groupingBy() 配合“下游收集器”(downstream collector),一行代码就能完成“先分类、再计算”的全过程,无需手动遍历 Map 的每个 value 列表。
用 groupingBy 指定分组键,再嵌套聚合操作
分组本身只返回 Map,但真正实用的是在分组的同时直接算出每组的统计值。关键在于第二个参数传入一个下游收集器:
- 按部门统计人数:
groupingBy(Person::getDept, Collectors.counting())→ 返回Map - 按城市求平均年龄:
groupingBy(Person::getCity, Collectors.averagingInt(Person::getAge))→ 返回Map - 按状态汇总薪资总额:
groupingBy(Order::getStatus, Collectors.summingDouble(Order::getAmount))→ 返回Map - 按年级获取最高分学生:
groupingBy(Student::getGrade, Collectors.maxBy(Comparator.comparing(Student::getScore)))→ 返回Map,取值时需调用> orElse(null)
一次获取多个统计指标:summarizingXXX
如果需要同时拿到总和、平均值、最大最小值、个数等,用 summarizingInt 等更高效——避免多次流操作,也省去自己封装对象的麻烦:
groupingBy(Product::getCategory, Collectors.summarizingDouble(Product::getPrice))- 返回
Map - 后续可链式调用:
stats.getSum()、stats.getAverage()、stats.getMax()等
对组内元素做定制化处理:collectingAndThen
当标准收集器不满足需求(比如要排序后取前3名、去重后再转 List),可用 collectingAndThen 包装:
- 每组按分数降序,取前两名:
groupingBy(Student::getSubject, collectingAndThen(toList(), list -> list.stream().sorted(comparing(Student::getScore).reversed()).limit(2).collect(toList()))) - 每组姓名去重并拼成字符串:
groupingBy(Team::getName, collectingAndThen(Collectors.toSet(), set -> String.join(",", set)))
注意空值与类型匹配
聚合结果类型由下游收集器决定,常见陷阱包括:
-
counting()返回Long,不是Integer;summingInt返回Integer,而summingDouble返回Double - 若分组字段可能为
null,建议提前filter(Objects::nonNull)或用groupingBy(Objects::toString)避免空指针 - 使用
maxBy/minBy时,value 类型是Optional,遍历时必须判空,否则运行时报NoSuchElementException
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











