Eran的答案描述了的两个参数和三个参数的区别reduce
,其中前者减小Stream<T>
到,T
而后者减小Stream<T>
到U
。但是,它实际上并没有解释的附加功能组合的需求减少时Stream<T>
至U
。
Streams API的设计原则之一是,顺序流和并行流之间的API不应有所不同,换句话说,特定的API不应阻止流顺次或并行地正确运行。如果您的lambda具有正确的属性(关联,无干扰等),则按顺序或并行运行的流应会提供相同的结果。
首先让我们考虑归约的两个参数版本:
T reduce(I, (T, T) -> T)
顺序实现很简单。标识值I
与第零个流元素“累加”以给出结果。该结果与第一流元素累加以给出另一个结果,该结果又与第二流元素累加,依此类推。累积最后一个元素后,将返回最终结果。
并行实现通过将流分成多个段开始。每个段都由自己的线程以上述顺序方式进行处理。现在,如果有N个线程,则有N个中间结果。这些需要减少到一个结果。由于每个中间结果都是T类型,并且我们有多个中间结果,因此我们可以使用相同的累加器函数将这N个中间结果减少为单个结果。
现在让我们考虑降低一个假设两ARG减少操作Stream<T>
来U
。在其他语言中,这称为“折叠”或“向左折叠”操作,因此我在这里将其称为。请注意,这在Java中不存在。
U foldLeft(I, (U, T) -> U)
(请注意,标识值I
的类型为U。)
的顺序版本foldLeft
类似于的顺序版本,reduce
不同之处在于中间值的类型为U而不是T。但是在其他方面相同。(假设的foldRight
操作将类似,除了操作将从右到左执行,而不是从左到右执行。)
现在考虑的并行版本foldLeft
。首先,将流分成多个部分。然后,我们可以让N个线程中的每一个将其段中的T值都减少为U型的N个中间值。现在呢?我们如何从N个类型的N值到U类型的单个结果?
缺少的是另一个将 U型的多个中间结果合并为U型的单个结果的函数。如果我们有一个将两个U值合并为一个的函数,则足以将任意数量的值缩减为一个-就像上面的原始还原。因此,给出不同类型结果的归约运算需要两个功能:
U reduce(I, (U, T) -> U, (U, U) -> U)
或者,使用Java语法:
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
总之,要对不同的结果类型进行并行归约,我们需要两个函数:一个函数将T元素累加到中间U值,第二个函数将中间U值组合成单个U结果。如果我们不切换类型,那么事实证明累加器功能与组合器功能相同。这就是为什么简化为相同类型仅具有累加器功能,而简化为不同类型则需要单独的累加器和组合器功能的原因。
最后,Java不提供foldLeft
和foldRight
操作,因为它们暗含了固有顺序的特定操作顺序。这与上述提供相同支持顺序和并行操作的API的设计原则相冲突。