reduce-歸約
看下詞典翻譯:
好的命名是自解釋的
reduce的方法取得就是其中歸納的含義
java8 流相關的操作中,我們把它理解 "累加器",之所以加引號是因為他並不僅僅是加法
他的運算可以是一個Lambda 表達式
所以更准確的說 reduce 是一個迭代運算器
Stream包的文檔中其實已經說的很明白了
但是就是因為不是很理解所以看的雲里霧里
其中說到:
一個reduce操作(也稱為折疊)接受一系列的輸入元素,並通過重復應用操作將它們組合成一個簡單的結果
參照reduce方法文檔給出的示例
T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
累計運算的概念
以下面的這個方法為例解析
BinaryOperator 是BiFunction 的三參數特殊化形式,兩個入參和返回結果都是類型T
計算1,2,3,4,5 的和,並且初始值為3
也就是計算3+1+2+3+4+5
|
1.使用Stream 兩個參數的reduce方法進行歸約運算
2.使用for循環迭代調用BinaryOperator 的apply進行運算
其實兩種方式背后的思維方式是一樣的
那就是
結果重新作為一個參數,不斷地參與到運算之中,直到最后結束
|
理解reduce的含義重點就在於理解"累 加 器" 的概念
只要能夠理解了累計運算的概念
就可以完全理解Stream 中reduce方法
他就是一個不斷累計運算的過程
Stream的一個參數和兩個參數的方法的基本邏輯都是如此
差別僅僅在於一個參數的是result R = T1 ,然后再繼續與剩下的元素參與運算
|
三個參數的reduce
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
它的形式類似於
與兩個參數的reduce不同的地方在於類型
雙參數的返回類型為T Stream類型為T
三參數的返回類型為U Stream類型為T 有了更大的發揮空間 T可能為U 也可能不是U
|
很顯然,三參數的reduce 方法的思維方式同雙參數的並無二致
所以問題來了,那還要第三個參數做什么?
其實第三個參數用於在並行計算下 合並各個線程的計算結果
並行流運行時:內部使用了fork-join框架
多線程時,多個線程同時參與運算
多個線程執行任務,必然會產生多個結果
那么如何將他們進行正確的合並
這就是第三個參數的作用
大致處理流程
從流程上看的 結果R是一直參與運算的!!
我們之前也有一個例子
兩種情況下的結果是不一樣的!!!!
那么這個方法不是有問題么?
其實不然,有問題的是我們的寫法
文檔中進行了明確的說明要求
翻譯下:
第一點:identity
的值對於合並運算combiner來說必須是一個恆等式,也就是說對於任意的u, combiner(identity,u) 和u是相同的
這句話看起來怪怪的,對於任意的u 經過合並運算 竟然還是u,那還要這個干嘛??
從我們上面的並行處理流程可以看得出來,這個result 的初始identity 對於每一個分支都是參與運算的! |
這也是為什么要求:
任意的u, combiner(identity,u) 和u是相同的
的原因
我們之所以會錯,就是因為沒有達到要求
我們的combiner為 (a,b)->a+b;
那么如果分為兩個分支進行運算,我們的初始值identity就參與了兩次運算 也就是說多加了兩個identity的值!!
怎么樣才能保證u = combiner(identity,u)
除非identity=0 這才是對於 (a,b)->a+b 來說能夠保障u = combiner(identity,u)
否則,你就不要用(a,b)->a+b 這個combiner
我們把Identity換成0之后
結果就不再有問題了
第二點
combiner 必須和accumulator要兼容
對於任意的u 和 t
這到底是什么意思呢?
場景
假設說4個元素 1,2,3,4 需要運算
此時假設已經 1,2,3 三組數據已經運算結束,馬上要同第四組運算
如果是並行,我們假定1,2,3 在一個分支 4單獨在另一分支
|
並行時
U為已經計算好的1,2,3后的結果 接下來要與另一組的4 合並
T4則是identity與T參與運算
上面的圖就是
combiner.apply(u, accumulator.apply(identity, t))
|
顯然這只是並行和非並行兩種不同的處理運算方式,他們應該是相同的
也就是