Collectors.groupingBy()是Java Stream API中用于按属性分组的核心工具,支持基础分组、分类+汇总(如计数、求和、平均值)、多级嵌套分组及空值处理。

在 Java 中,Collectors.groupingBy() 是 Stream API 提供的最常用分组工具之一,能快速按对象某个属性将列表划分为多个子集合,并支持进一步聚合(如求和、计数、平均值等)。关键在于理解其三个重载版本的适用场景,以及如何组合下游收集器实现“分类+汇总”。
基础分组:按单个属性生成 Map>
这是最常见用法,适用于简单归类。假设有一个员工列表,需按部门分组:
List<Employee> employees = Arrays.asList(
new Employee("张三", "研发部", 15000),
new Employee("李四", "销售部", 8000),
new Employee("王五", "研发部", 18000)
);
Map<String, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// 结果:{"研发部": [张三, 王五], "销售部": [李四]}
分类 + 汇总:用下游收集器计算统计值
真正体现“汇总”能力的地方。通过第二个参数传入下游收集器,可直接得到每组的统计结果,避免后续遍历:
-
统计每组数量:
Collectors.counting() -
求每组薪资总和:
Collectors.summingDouble(Employee::getSalary) -
计算每组平均薪资:
Collectors.averagingDouble(Employee::getSalary) -
获取每组最高/最低薪资员工:
Collectors.maxBy(Comparator.comparing(Employee::getSalary))
示例:按部门汇总总薪资
立即学习“Java免费学习笔记(深入)”;
Map<String, Double> deptSalarySum = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.summingDouble(Employee::getSalary)
));
// 结果:{"研发部": 33000.0, "销售部": 8000.0}
多级分组与嵌套汇总
当需要按多个维度分组(如“先按部门,再按职级”),可嵌套使用 groupingBy:
Map<String, Map<String, Long>> deptAndLevelCount = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(
Employee::getLevel,
Collectors.counting()
)
));
// 结果:{"研发部": {"高级": 2}, "销售部": {"中级": 1}}
注意:内层 groupingBy 作为下游收集器,返回的是 Map<k2 v2></k2>,整体类型为 Map<k1 map v2>></k1>。
处理空值与自定义分组键
若分组属性可能为 null,默认会抛 NullPointerException。可借助 Function.identity() 或自定义映射函数规避:
- 用
Objects.toString(emp.getDepartment(), "未知")将 null 转为默认值 - 使用
Collectors.groupingBy(emp -> emp.getDepartment() != null ? emp.getDepartment() : "未分配") - 若需保持插入顺序,可指定 map 工厂:
Collectors.groupingBy(..., LinkedHashMap::new, ...)
不复杂但容易忽略。


















