Java Stream API的Collectors.groupingBy支持多层级分组,本质是嵌套使用groupingBy,每层对应一个分类维度,最终生成嵌套Map结构;单层按部门分组得Map<String, List<Employee>>,两层嵌套(如部门→职位)得Map<String, Map<String, List<Employee>>>,三层及以上可继续递进嵌套;每层可搭配不同下游收集器(如counting、averagingDouble)优化结果结构。

Java Stream API 的 Collectors.groupingBy 支持多层级分组,本质是嵌套使用 groupingBy,每层对应一个分类维度,最终生成嵌套的 Map 结构。
基础单层分组
最常见用法是按单一字段分组,例如按部门划分员工列表:
Map<String, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));结果是一个 Map<String, List<Employee>>,键为部门名,值为该部门员工列表。
两层嵌套分组(常用)
先按一级字段分组,再对每个子组按二级字段进一步分组。例如:先按部门,再按职位:
立即学习“Java免费学习笔记(深入)”;
- 外层 groupingBy 返回
Map<String, Map<String, List<Employee>>> - 内层 groupingBy 作为下游收集器传入,写在第二个参数位置
Map<String, Map<String, List<Employee>>> byDeptAndRole = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(Employee::getRole)
));访问方式如:byDeptAndRole.get("Tech").get("Engineer") 获取技术部工程师列表。
三层及以上分组(递进嵌套)
继续在内层 groupingBy 中嵌套新的 groupingBy 即可。例如:部门 → 职位 → 年龄段(按 10 年分段):
Map<String, Map<String, Map<Integer, List<Employee>>>> result = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(
Employee::getRole,
Collectors.groupingBy(e -> e.getAge() / 10 * 10) // 如 25→20,37→30
)
));注意:每增加一层,返回类型就多一层 Map 嵌套,读取时需逐级调用 get。
结合下游收集器优化结构
默认每层都生成 List,但可替换为其他收集器以减少内存或满足业务需求:
- 用
Collectors.counting()统计数量而非保存对象 - 用
Collectors.summingInt(e -> e.getSalary())汇总薪资 - 用
Collectors.mapping(..., Collectors.toList())提前投影字段
例如:部门 → 职位 → 各职位平均薪资:
Map<String, Map<String, Double>> avgSalaryByDeptAndRole = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(
Employee::getRole,
Collectors.averagingDouble(Employee::getSalary)
)
));多层级 groupingBy 的核心是“层层嵌套、逐级细化”,关键在于理清分组维度顺序和下游收集器的组合方式,不复杂但容易忽略类型推导和空值处理。


















