groupingBy本身不过滤,过滤需用filter()(分组前全局过滤)或filtering(分组后各组独立过滤);错误地在mapping中条件返回null会引发异常。
Java Stream API 中,groupingBy 本身不负责过滤,它只做分组;真正执行过滤逻辑的,是配合使用的下游 Collector(如 filtering)或前置的 filter() 操作。关键区别在于:前者在每个分组内部过滤,后者在分组前全局过滤——选错位置会导致结果偏差。
适用于“先筛再分”的场景,比如剔除无效数据后再按属性归类。
.collect(Collectors.groupingBy(...)) 之前调用ACTIVE 的用户,并按部门分组users.stream()
.filter(u -> "ACTIVE".equals(u.getStatus()))
.collect(Collectors.groupingBy(User::getDepartment));
适用于“分组后按条件保留部分元素”,比如每个部门只保留薪资高于阈值的员工。
立即学习“Java免费学习笔记(深入)”;
groupingBy 的下游 Collector 使用Collectors.filtering 是 Java 9 引入)users.stream()
.collect(Collectors.groupingBy(
User::getDepartment,
Collectors.filtering(u -> u.getSalary() > 15000, Collectors.toList())
));
有人试图在 mapping 中用条件判断来“选择性添加”,例如:
// ❌ 错误:有副作用,且逻辑混乱
Collectors.mapping(u -> {
if (u.getAge() > 18) return u;
else return null; // null 会引发异常
}, Collectors.toList())
这不仅破坏函数式风格,还可能抛出 NullPointerException。正确做法是把过滤逻辑交给 filtering 或前置 filter。
如果分组后只需提取某个字段(如 ID),而不是整个对象,可链式组合:
getId())orders.stream()
.collect(Collectors.groupingBy(
Order::getStatus,
Collectors.filtering(o -> o.getAmount() > 1000,
Collectors.mapping(Order::getId, Collectors.toList())
)
));