Java中ArrayList通过Stream实现复杂分组的核心是Collectors.groupingBy的嵌套与组合,配合自定义分类器、下游收集器(如mapping、filtering、summingInt等)及多级分组逻辑。

Java 中 ArrayList 通过 Stream 实现复杂分组,核心是 Collectors.groupingBy 的嵌套与组合,配合自定义分类器、下游收集器(如 mapping、filtering、summingInt 等)以及多级分组逻辑。
基础分组:按单个字段分类
最常见的是按对象某个属性分组,比如将用户列表按城市分组:
List<User> users = Arrays.asList(
new User("张三", "北京", 25),
new User("李四", "上海", 30),
new User("王五", "北京", 28)
);
Map<String, List<User>> byCity = users.stream()
.collect(Collectors.groupingBy(User::getCity));
// 结果:{"北京": [张三, 王五], "上海": [李四]}
多级分组:嵌套 groupingBy
实现“先按城市,再按年龄区间”这类两级分组,用 groupingBy 嵌套即可:
Map<String, Map<String, List<User>>> grouped = users.stream()
.collect(Collectors.groupingBy(
User::getCity,
Collectors.groupingBy(u -> u.getAge() < 28 ? "青年" : "成年")
));
- 外层 key 是城市,内层 key 是年龄段
- 注意第二参数是下游收集器,不是另一个 lambda
带聚合的分组:统计、映射、去重
不只保留原始对象,还能做转换或计算:
立即学习“Java免费学习笔记(深入)”;
-
统计每组人数:
Collectors.counting() -
提取某字段并去重:
Collectors.mapping(User::getName, Collectors.toSet()) -
求年龄总和:
Collectors.summingInt(User::getAge) -
只保留满足条件的元素:
Collectors.filtering(u -> u.getAge() > 26, Collectors.toList())
例如:按城市分组,并统计各城市 25 岁以上用户姓名集合:
Map<String, Set<String>> cityToNames = users.stream()
.filter(u -> u.getAge() > 25)
.collect(Collectors.groupingBy(
User::getCity,
Collectors.mapping(User::getName, Collectors.toSet())
));
自定义分组逻辑:用 Function 或 if-else 拆分
当分组规则较复杂(如按分数段、状态组合、多条件判断),可写一个明确的分类方法:
private static String getCategory(User u) {
if (u.getAge() < 25) return "Z";
else if (u.getAge() <= 30) return "M";
else return "S";
}
Map<String, List<User>> byCategory = users.stream()
.collect(Collectors.groupingBy(Main::getCategory));
也可用 lambda 内联表达,但建议提取方法提升可读性与复用性。
关键点在于理解 groupingBy 的三个重载版本,尤其是带下游收集器的二参/三参形式;合理组合 mapping、filtering、collectingAndThen 等,就能应对绝大多数业务分组场景。


















