Scala中reduce()与reduceLeft()为何对序列的归约表现一致?二者的核心差异是什么?
Scala中
reduce与reduceLeft的核心区别 这个问题问得特别到位!很多刚接触Scala集合归约操作的开发者都会有这个误解,我来给你把两者的差异拆解清楚:
1. 执行顺序的确定性是核心差异
reduceLeft:严格左关联,顺序完全固定
不管你用的是串行还是并行集合,reduceLeft都会严格按照从左到右的顺序执行归约:第一个元素作为初始值,和第二个元素运算,结果再和第三个元素运算,以此类推。就像你例子里的List(1,4,3,9),它的执行路径只能是((1+4)+3)+9,所以输出必然是1 + 4→5 + 3→8 + 9。reduce:顺序不固定,取决于集合类型和上下文reduce的设计初衷是支持并行归约,所以它不保证执行顺序:- 对于串行集合(比如普通
List),Scala默认用reduceLeft的逻辑实现reduce,所以你看到的输出和reduceLeft完全一致; - 但如果是并行集合(比如
ParList),reduce会把集合分成多个子块并行计算,再合并结果。这时候就会出现你最初预想的分组归约,比如(1+4)和(3+9)先分别计算,再把两个结果相加。
- 对于串行集合(比如普通
2. 操作符的可结合性决定结果一致性
你最初的误解其实来源于对reduce并行特性的想象,但这里有个关键前提:reduce要求传入的归约函数是可结合的(比如加法、乘法),这样不管并行还是串行计算,最终结果都是一致的。
但如果函数是不可结合的(比如减法、除法),reduce和reduceLeft的结果就会天差地别:
// reduceLeft严格左到右:10 - 5 = 5,再5 - 3 = 2 List(10, 5, 3).reduceLeft(_ - _) // 结果:2 // 串行时和reduceLeft一致,但并行时可能变成10 - (5 - 3) = 8 List(10, 5, 3).reduce(_ - _) // 串行结果2,并行结果可能为8
3. 语义与使用场景
- 如果你需要明确的左关联执行顺序,或者你的归约函数不可结合,一定要用
reduceLeft; - 如果你想利用并行计算提升性能,且函数是可结合的,就用
reduce; - 另外注意:两者都要求集合非空,否则会抛出
UnsupportedOperationException,处理空集合建议用reduceOption或带初始值的foldLeft/fold。
举个并行集合的例子验证差异:
import scala.collection.parallel.ParList ParList(1,4,3,9).reduce {(a, b) => println(s"$a + $b"); a + b}
这时候输出可能会是(顺序不固定):
1 + 4 3 + 9 5 + 12
完全符合你最初对reduce归约方式的预想。
内容的提问来源于stack exchange,提问作者sungjun cho
相关产品推荐
相关产品推荐

