Eran's answer描述的兩ARG和前者的reduce
三ARG版本之間的差異減少Stream<T>
到T
而後者將Stream<T>
減少爲U
。但是,在將Stream<T>
減小爲U
時,實際上並未解釋需要額外的組合器功能。
Streams API的設計原則之一是,API不應該在順序流和並行流之間不同,或者換句話說,特定的API不應該阻止順序或並行正確運行流。如果您的lambda具有正確的屬性(關聯,不干擾等),則順序運行或並行運行的流應該得到相同的結果。
我們先考慮減少兩ARG版本:
T reduce(I, (T, T) -> T)
順序實現非常簡單。標識值I
與第零個流元素「累積」以給出結果。該結果與第一個流元素一起累加以給出另一個結果,該結果又與第二個流元素一起累積,等等。最後一個元素累積後,返回最終結果。
並行實現通過將流拆分爲段開始。每段都以自己的線程按照上面描述的順序方式進行處理。現在,如果我們有N個線程,我們有N箇中間結果。這些需要減少到一個結果。由於每個中間結果都是T型的,並且我們有幾個,我們可以使用相同的累加器函數將這N箇中間結果減少到單個結果。
現在讓我們考慮一個假設的雙參數歸約操作,將Stream<T>
減小爲U
。在其他語言中,這被稱爲「摺疊」或「摺疊式」操作,所以這就是我在這裏所說的。注意這在Java中不存在。
U foldLeft(I, (U, T) -> U)
(注意,標識值是I
類型U的)
的foldLeft
的順序版本就像的reduce
除了順序版本,所述中間值是U型的,而不是T類型的但其他方面是一樣的。 (假設的foldRight
操作將類似,但操作將從右到左而不是從左到右執行。)
現在考慮foldLeft
的並行版本。我們首先將流分成多個段。然後,我們可以讓N個線程中的每一個線程將其段中的T值縮減爲N個U類型的中間值。現在呢?我們如何從U型的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的設計原則相沖突。
相關問題:https://stackoverflow.com/questions/24202473/does-a-sequential-stream-in-java-8-use-the-combiner-parameter-on-calling-collect – nosid
啊哈,這是爲並行流......我稱之爲泄漏抽象! – Andy