Arrays.stream().parallel().reduce()可高效实现数值数组并行求和,通过初始值、累加器和组合器支持自定义归并逻辑,需确保identity恒等、accumulator和combiner满足结合律与交换律。

可以用 Arrays.stream().parallel().reduce() 高效实现数值数组的并行求和,同时通过提供合适的初始值、累加器(accumulator)和组合器(combiner),支持自定义归并逻辑。
并行求和:基础用法
对 int[] 或 double[] 等基本类型数组,并行求和最简洁的方式是转为对应流后调用 reduce:
int sum = Arrays.stream(arr).parallel().reduce(0, Integer::sum);double total = Arrays.stream(doubles).parallel().reduce(0.0, Double::sum);
注意:必须显式调用 .parallel(),否则仍是串行;Integer::sum 是累加器,把当前部分结果与新元素相加;0 是恒等初始值(满足 op(identity, x) == x)。
自定义归并:三参数 reduce 的关键
当归并逻辑不是简单加法(比如求最大绝对值、加权平均、拼接字符串等),需使用三参数 reduce(identity, accumulator, combiner):
- identity:各线程局部归并的起始值(必须满足结合律且是恒等元)
-
accumulator:将一个元素合并进当前局部结果(
(result, element) → newResult) -
combiner:将两个局部结果合并(
(result1, result2) → mergedResult),必须满足结合律与交换律
例如,并行计算整数数组中所有元素的平方和:
int sqSum = Arrays.stream(arr)
.parallel()
.reduce(0, // identity
(acc, x) -> acc + x * x, // accumulator
(a, b) -> a + b); // combiner(加法满足结合+交换)
注意事项与常见陷阱
使用 reduce 并行归并时需特别注意:
- 初始值
identity必须对所有可能的中间结果保持恒等性;若误用非恒等值(如用1求和),会导致错误结果 -
combiner必须能安全合并任意两个局部结果,不能依赖顺序或共享状态 - 对对象数组(如
String[]),避免用String::concat作累加器——它不满足结合律("a".concat("b".concat("c")) ≠ ("a".concat("b")).concat("c")在语义上虽等价,但作为函数引用时若未保证不可变性,可能引发并发问题;推荐用StringBuilder或直接用Collectors.joining() - 若归并操作本身有副作用(如修改外部变量),结果不可预测,应严格避免
替代方案:考虑 collect() 更直观
对于复杂归并(如统计、分组、构建集合),collect() 通常比 reduce() 更清晰、更安全。例如并行求和也可写成:
int sum = Arrays.stream(arr)
.parallel()
.collect(() -> new AtomicInteger(0), // supplier
(ai, x) -> ai.addAndGet(x), // accumulator
(a, b) -> a.addAndGet(b.get())); // combiner
但除非需要极致控制或已有现成归并函数,一般优先选 reduce 的函数式风格。


















