Collectors.groupingBy通过嵌套下游收集器实现多级分组,形成Map套Map结构;支持两级、三级甚至更多层级,最内层可结合counting、summingInt、mapping等增强实用性。

Java 中 Collectors.groupingBy 本身只支持单级分组,但通过嵌套调用可以自然实现多级分组——本质是“分组结果的值再次被分组”,形成 Map 套 Map 的结构。
两级分组:外层按部门,内层按职位
假设有一个员工列表,想先按 department 分组,再在每个部门内按 position 分组:
Map<String, Map<String, List<Employee>>> deptToPositionToEmployees =
employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(Employee::getPosition)
));说明:
- 第一个参数 Employee::getDepartment 是外层分组键;
- 第二个参数是下游收集器(即对每个部门内的员工再执行一次 groupingBy);
- 结果类型为 Map<String, Map<String, List<Employee>>>。
三级分组:嵌套三层 groupingBy
若还需按年龄区间(如 "20-30"、"31-40")进一步细分,可继续嵌套:
Map<String, Map<String, Map<String, List<Employee>>>> result =
employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(
Employee::getPosition,
Collectors.groupingBy(e -> {
int age = e.getAge();
if (age <= 30) return "20-30";
else if (age <= 40) return "31-40";
else return "41+";
})
)
));注意:
- 每一层 groupingBy 都作为上一层的下游收集器;
- 类型会逐层加深,读写时需注意空指针(建议配合 Optional 或判空处理);
- 键函数支持任意表达式或方法引用,不局限于字段。
结合 downstream 收集器增强实用性
多级分组中,最内层不一定非要是 List。例如统计每部门每职位的人数:
立即学习“Java免费学习笔记(深入)”;
- 用
Collectors.counting()替换最内层的groupingBy:
Map<String, Map<String, Long>> deptToPositionToCount =
employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(
Employee::getPosition,
Collectors.counting()
)
));- 也可用
Collectors.summingInt求薪资总和、Collectors.mapping提取姓名列表等。
使用 groupingBy + mapping 简化嵌套逻辑
如果只是想“分组后提取某个属性”,避免深层 Map,可用 mapping 配合 toList 或 toSet:
Map<String, Map<String, Set<String>>> deptToPositionToNames =
employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(
Employee::getPosition,
Collectors.mapping(Employee::getName, Collectors.toSet())
)
));这样内层不再是 List<Employee>,而是更轻量的 Set<String>,适合只关心名称、去重等场景。


















