partitioningBy()仅支持二元分区,将元素按Predicate分为true/false两组并返回Map;适用于非此即彼场景(如有效/无效订单),不适用于多类别分类;需防御null,且仅当需同时处理两组时才应使用。

partitioningBy() 的核心行为和适用场景
Collectors.partitioningBy() 不是通用分组工具,它只接受 Predicate(返回 boolean 的函数),强制将流元素分成两组:满足条件的进 true 键,不满足的进 false 键。结果是 Map<boolean list>></boolean>,键只有两个可能值——这点必须明确,否则容易误用成 groupingBy()。
它适合「非此即彼」的二元切分,比如:有效/无效订单、已激活/未激活用户、正数/非正数、字符串是否为空等。不适合多类别分类(如按状态分“待审/审核中/已通过/已拒绝”)。
基础用法与常见错误写法
最简形式就是传一个 Predicate,但要注意:如果传入的 lambda 或方法引用本身抛异常(比如对 null 调用 .length()),整个收集会失败,不会跳过或静默处理。
- ✅ 正确(防御
null):partitioningBy(Objects::nonNull)或partitioningBy(s -> s != null && s.length() > 0) - ❌ 危险(可能 NPE):
partitioningBy(s -> s.length() > 0)—— 当流中有null字符串时直接抛NullPointerException - ⚠️ 注意返回类型:别试图强转成
HashMap或其他具体实现;它返回的是不可变视图的Map,底层实现由 JDK 决定
如何获取其中一组而不构建完整 Map
如果你只需要 true 分组的列表(比如只关心“合格数据”),没必要保留整个 Map。可以链式调用后立即取值:
List<String> valid = list.stream()
.collect(Collectors.partitioningBy(s -> s != null && !s.trim().isEmpty()))
.get(true); // 可能为 null,如果没一个元素满足条件
但更安全的做法是用 Optional.ofNullable() 包一层,或改用 filter().toList() —— 如果你根本不需要 false 组,partitioningBy() 就是过度设计。
- 用
partitioningBy()的真实理由通常是:**同时需要两组结果**,且后续要分别处理(比如分别入库、分别计数、分别发消息) - 如果只取一组,且流不大,
filter().toList()更直观、内存开销更低 -
.get(true)返回null当且仅当流为空或无一匹配——不是 bug,是设计如此
性能与线程安全注意事项
在并行流中使用 partitioningBy() 是安全的,JDK 会保证最终合并逻辑正确。但注意两点:
- 内部仍会创建两个独立的
ArrayList实例来累积数据,所以内存占用是两份,不是一份流遍历 + 一次分发 - 如果 predicate 计算代价高(比如涉及远程调用或复杂正则),别指望 JVM 会自动缓存结果;应提前计算好布尔字段再流式处理
- 不要在 predicate 中修改外部状态(如 ++ 计数器),并行时结果不可预测
真正省时间的地方在于语义清晰和代码简洁,而不是算法复杂度优化——它的时间复杂度仍是 O(n),和手写 for 循环一样。

















