partitioningBy()仅分true/false两组,返回固定键为true/false的Map;需注意null安全、谓词纯函数性、下游收集器兼容性及并行流安全性。

partitioningBy() 的核心行为:只分 true/false 两组
Collectors.partitioningBy() 本质是二元分类器,它不支持自定义分组键,只接受一个 Predicate,返回一个 Map<boolean list>></boolean>。key 固定为 true 和 false,value 分别是满足和不满足条件的元素列表。这不是分组(grouping),不能扩展成多组——想按状态、类型、范围分多类,得用 Collectors.groupingBy()。
基础用法:传入 lambda 即可,但注意 null 安全
常见错误是谓词内部抛 NullPointerException,比如对可能为 null 的字段直接调用方法:
list.stream()
.collect(Collectors.partitioningBy(item -> item.getName().length() > 5)); // ❌ item 或 getName() 可能为 null
应显式判空:
list.stream()
.collect(Collectors.partitioningBy(item -> item != null
&& item.getName() != null
&& item.getName().length() > 5)); // ✅
- 谓词必须是纯函数,不能有副作用(如修改外部变量)
- 返回的
Map是不可变的(具体实现是HashMap,但接口返回的是不可变视图) - 如果流为空,结果是
{true=[], false=[]},两个 key 都存在
进阶用法:嵌套 partitioningBy + downstream 收集器
第二个参数允许你指定下游收集器,把每组内部再做聚合。例如,不只要分组,还要统计每组数量:
Map<Boolean, Long> counts = list.stream()
.collect(Collectors.partitioningBy(
item -> item.getAge() >= 18,
Collectors.counting())); // ✅ 返回 Map<Boolean, Long>
或者把满足条件的元素转成 Set 去重:
Map<Boolean, Set<String>> namesByStatus = list.stream()
.collect(Collectors.partitioningBy(
item -> item.isActive(),
Collectors.mapping(Item::getName, Collectors.toSet())));
- 下游收集器不能是
toMap()这类可能抛重复 key 异常的(因为 partitioningBy 固定只有两个 key) - 若下游用
Collectors.toList()(默认行为),可省略第二个参数 - 下游收集器的泛型类型必须与原始元素类型兼容,否则编译报错
容易忽略的坑:顺序、并发与空值处理
分区结果中,true 组的元素顺序与原流中首次出现顺序一致,false 组同理——但两组之间无全局顺序保证(不过实践中通常保持输入顺序)。更大的隐患在并行流:
list.parallelStream()
.collect(Collectors.partitioningBy(...)); // ⚠️ 结果正确,但下游收集器必须是无状态且可合并的
-
partitioningBy在并行流下仍安全,但下游若用了非线程安全的收集器(如自定义的非同步ArrayList构造器),会出错 - 如果元素本身是可变对象,且谓词依赖其可变状态,多次执行可能得到不同结果
- 返回的
Map不包含null键,但若谓词逻辑里产生null(比如Objects.equals(a, b)中 a 为 null),不会导致异常,只是影响分组逻辑
真正麻烦的是混合 null 处理和下游映射:一旦 mapping 的函数返回 null,toSet() 没问题,但 toList() 会照收,而某些下游收集器(如 toMap())会直接炸——不过 partitioningBy 不允许配 toMap() 作下游,所以这一步其实被语法拦住了。

















