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>> byDept = people.stream()
.collect(Collectors.groupingBy(Person::getDepartment));
// 结果:{"IT": [Alice, Charlie], "HR": [Bob]}
</string></person>
二级分组:嵌套 groupingBy 实现多层结构
把第二个参数传入另一个 Collectors.groupingBy,就能构建 Map
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 按部门分组 → 再按年龄区间分组(如 20–29、30–39)
- 注意内层 groupingBy 的分类函数作用于外层分组后的子流元素
Map<string map list>>> deptAndAgeGroup = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.groupingBy(p -> p.getAge()
<h3>聚合统计:替换默认 List 收集器</h3>
<p>第三个参数可指定下游收集器(down<a style="color:#f60; text-decoration:underline;" title="stream" href="https://m.php.cn/zt/19633.html" target="_blank">stream</a>),把每组的 <code>List<t></t></code> 替换为更紧凑的结果:</p>
<ul>
<li>
<code>Collectors.counting()</code> → 统计每组数量(<code>Map<k long></k></code>)</li>
<li>
<code>Collectors.summingInt(Person::getAge)</code> → 求每组年龄总和</li>
<li>
<code>Collectors.averagingDouble(Person::getAge)</code> → 计算平均年龄</li>
<li>
<code>Collectors.mapping(..., Collectors.toList())</code> → 提取字段再收集(如只存姓名)</li>
</ul>
<pre class="brush:java;toolbar:false;">// 部门 → 平均年龄
Map<string double> avgAgeByDept = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.averagingDouble(Person::getAge)
));
// 部门 → 姓名列表(不保留 Person 对象)
Map<string list>> namesByDept = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.mapping(Person::getName, Collectors.toList())
));
</string></string>
处理空值与自定义 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()
));
</string>
不复杂但容易忽略的是:groupingBy 本质是“先分组、再对每组子流做 collect”,所有下游操作都作用于子流。理清这个逻辑,组合出任意层级、任意聚合的分组统计就水到渠成。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










