Collectors.groupingBy是Java Stream API中用于复杂分组统计的核心工具,支持单字段分组、嵌套多级分组及下游聚合(如计数、求和、平均、映射等),并可处理null键与自定义Map类型。

Java 中 Collectors.groupingBy 是 Stream API 最常用也最灵活的分组工具之一,它不仅能按单字段分组,还能嵌套分组、多级统计、结合其他收集器做聚合,实现真正意义上的“复杂分组统计”。关键在于理解它的三个重载版本和组合用法。
基础分组:按属性值分类
最简单的用法是传入一个分类函数(Function),将流中元素按返回值分组,结果是 Map<k list>></k>:
List<Person> people = Arrays.asList(
new Person("Alice", "IT", 28),
new Person("Bob", "HR", 32),
new Person("Charlie", "IT", 25)
);
// 按部门分组
Map<String, List<Person>> byDept = people.stream()
.collect(Collectors.groupingBy(Person::getDepartment));
// 结果:{"IT": [Alice, Charlie], "HR": [Bob]}
二级分组:嵌套 groupingBy 实现多层结构
把第二个参数传入另一个 Collectors.groupingBy,就能构建 Map
- 按部门分组 → 再按年龄区间分组(如 20–29、30–39)
- 注意内层 groupingBy 的分类函数作用于外层分组后的子流元素
Map<String, Map<String, List<Person>>> deptAndAgeGroup = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.groupingBy(p -> p.getAge() < 30 ? "Young" : "Senior")
));
// 结果:{"IT": {"Young": [Alice, Charlie], "Senior": []}, "HR": {"Young": [], "Senior": [Bob]}}
聚合统计:替换默认 List 收集器
第三个参数可指定下游收集器(downstream),把每组的 List<t></t> 替换为更紧凑的结果:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
立即学习“Java免费学习笔记(深入)”;
-
Collectors.counting()→ 统计每组数量(Map<k long></k>) -
Collectors.summingInt(Person::getAge)→ 求每组年龄总和 -
Collectors.averagingDouble(Person::getAge)→ 计算平均年龄 -
Collectors.mapping(..., Collectors.toList())→ 提取字段再收集(如只存姓名)
// 部门 → 平均年龄
Map<String, Double> avgAgeByDept = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.averagingDouble(Person::getAge)
));
// 部门 → 姓名列表(不保留 Person 对象)
Map<String, List<String>> namesByDept = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.mapping(Person::getName, Collectors.toList())
));
处理空值与自定义 Map 类型
默认情况下,null 分类键会抛 NPE;若需支持 null,可用 Collectors.groupingBy(keyMapper, LinkedHashMap::new, downstream):
- 第一个参数:分类函数(可返回 null)
- 第二个参数:Map 工厂(如
TreeMap::new保证有序,LinkedHashMap::new保持插入顺序) - 第三个参数:下游收集器(同前)
// 允许 department 为 null,并用 TreeMap 保证 key 字典序
Map<String, Long> countByDeptSorted = people.stream()
.collect(Collectors.groupingBy(
p -> p.getDepartment(), // 可能返回 null
TreeMap::new,
Collectors.counting()
));
不复杂但容易忽略的是:groupingBy 本质是“先分组、再对每组子流做 collect”,所有下游操作都作用于子流。理清这个逻辑,组合出任意层级、任意聚合的分组统计就水到渠成。

















