Java Stream.reduce() 的三种重载形式怎么选
直接用 reduce() 做聚合,不看参数签名容易出空指针或逻辑错。它有三个常见重载:
-
reduce(U identity, BinaryOperator accumulator):适合有明确初始值、且结果类型与元素类型一致(如Integer::sum) -
reduce(BinaryOperator accumulator):无初始值,返回Optional;空集合时不会抛异常,但你得手动处理Optional.empty() -
reduce(U identity, BiFunction accumulator, BinaryOperator combiner):并行流必须提供combiner,否则结果不可靠(比如用String::concat作 accumulator 却漏写 combiner,在 parallelStream 中可能拼错顺序)
字符串拼接用 reduce 为什么有时结果为空或乱序
常见错误是把 String::concat 当成线程安全的累加器直接用于并行流。它满足结合律但不满足“可分割性”——combiner 必须显式定义为 (a, b) -> a + b 或复用 String::concat,否则并行分支合并时行为未定义。
更稳妥的做法是用 Collectors.joining(),但如果坚持用 reduce:
- 串行:用
list.stream().reduce("", (a, b) -> a + b, String::concat) - 并行:必须三参数,且
combiner不能省略,哪怕和 accumulator 相同 - 注意
""是 identity,若集合为空则返回空字符串;若想返回null,得改用两参数版再调orElse(null)
自定义对象聚合时 identity 参数为什么不能用 new XXX()
当聚合结果是自定义类(如 SumCount),identity 若写成 new SumCount(0, 0),在并行流中会被多个线程共享修改,导致数据竞争。正确做法是让 identity 是不可变对象,或改用三参数版,让 combiner 负责合并两个中间结果。