java中collectors.groupingby配合collectors.counting()可实现分组计数,返回map;支持带filter筛选、多级嵌套分组,但分组键不能为null,且结果不含未出现的分组。

Java 中 Collectors.groupingBy 配合下游收集器(downstream collector)实现计数,核心就是用 Collectors.counting() 作为第二个参数。它不生成元素列表,直接返回每组的元素个数,类型是 Map
基础写法:一行完成分组+计数
这是最常用也最推荐的方式:
- 分组键由第一个参数(
Function)决定,比如Employee::getDepartment - 计数逻辑由第二个参数
Collectors.counting()承担,它自动对每组内元素数量求和并返回Long - 结果中不会出现值为
0的条目——没有匹配的分组根本不会出现在 Map 里
示例:
Map.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));
带筛选条件的计数
如果只想统计满足某条件的数据(比如在职员工、年龄大于18岁),把 filter 放在 stream() 链开头即可:
-
filter必须在collect之前,否则 Stream 已消费完毕,无法重用 - 分组键仍按原始字段取值,但只有通过筛选的元素参与计数
示例:
Map.filter(Employee::isOnboard)
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));
多级嵌套分组计数
需要“先按 A 分,再按 B 统计”时,把另一个 groupingBy 当作下游收集器嵌套进去:
- 外层分组键(如部门)对应第一层 Map 的 key
- 内层分组逻辑(如薪资区间)作为第二层 key,
counting()在最内层生效 - 最终类型是
Map<string map long>></string>
示例(部门 → 薪资段 → 人数):
MapCollectors.groupingBy(Employee::getDepartment,
Collectors.groupingBy(e -> {
if (e.getSalary() else if (e.getSalary() else return "高于10000";
}, Collectors.counting())
)
);
注意 null 键和类型安全
分组键不能为 null,否则抛 NullPointerException:
- 若源数据中分组字段可能为空(如
getDepartment()返回null),需提前处理,例如:e -> Optional.ofNullable(e.getDepartment()).orElse("UNKNOWN") - 计数值固定是
Long,不是Integer;强转前要确认数量级,避免溢出 - 结果 Map 中不含未出现的分组(哪怕期望中有默认项),如需补全,得额外做 map.merge 或 post-process
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











