[五]java函數式編程歸約reduce概念原理 stream reduce方法詳解 reduce三個參數的reduce方法如何使用


reduce-歸約


看下詞典翻譯:
image_5b7bb842_2b7a
好的命名是自解釋的
reduce的方法取得就是其中歸納的含義
java8 流相關的操作中,我們把它理解 "累加器",之所以加引號是因為他並不僅僅是加法
他的運算可以是一個Lambda 表達式
所以更准確的說 reduce 是一個迭代運算器
Stream包的文檔中其實已經說的很明白了
但是就是因為不是很理解所以看的雲里霧里
其中說到:
一個reduce操作(也稱為折疊)接受一系列的輸入元素,並通過重復應用操作將它們組合成一個簡單的結果
 
參照reduce方法文檔給出的示例

T result = identity;

for (T element : this stream)

result = accumulator.apply(result, element)

return result;

 

累計運算的概念


以下面的這個方法為例解析
image_5b7bb842_61a1
BinaryOperator 是BiFunction 的三參數特殊化形式,兩個入參和返回結果都是類型T
 
計算1,2,3,4,5 的和,並且初始值為3  
也就是計算3+1+2+3+4+5 
1.使用Stream 兩個參數的reduce方法進行歸約運算
2.使用for循環迭代調用BinaryOperator 的apply進行運算
image_5b7bb842_27a8
其實兩種方式背后的思維方式是一樣的
那就是   
結果重新作為一個參數,不斷地參與到運算之中,直到最后結束
 
理解reduce的含義重點就在於理解"累   加   器" 的概念
image_5b7bb842_5f93
 
只要能夠理解了累計運算的概念
就可以完全理解Stream 中reduce方法
他就是一個不斷累計運算的過程
image_5b7bb842_f7c
 
 
Stream的一個參數和兩個參數的方法的基本邏輯都是如此
差別僅僅在於一個參數的是result  R = T1 ,然后再繼續與剩下的元素參與運算
 
 

三個參數的reduce


    <U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);
 
image_5b7bb842_57b2
 
它的形式類似於
image_5b7bb842_1a72
與兩個參數的reduce不同的地方在於類型
雙參數的返回類型為T  Stream類型為T
三參數的返回類型為U  Stream類型為T   有了更大的發揮空間  T可能為U 也可能不是U 
 
很顯然,三參數的reduce 方法的思維方式同雙參數的並無二致
所以問題來了,那還要第三個參數做什么?
其實第三個參數用於在並行計算下 合並各個線程的計算結果
 
並行流運行時:內部使用了fork-join框架
image_5b7bb842_567a
多線程時,多個線程同時參與運算
多個線程執行任務,必然會產生多個結果
那么如何將他們進行正確的合並
這就是第三個參數的作用
 
 
大致處理流程
image_5b7bb842_523b
從流程上看的 結果R是一直參與運算的!!
我們之前也有一個例子
兩種情況下的結果是不一樣的!!!!
image_5b7bb842_595b
 
image_5b7bb842_7701
結果不同  是因為  ((((5+1)+2)+3)+4)+5   和   (5+1)+ (5+2)+ (5+3)+ (5+4)+ (5+5)  運算結果不相同 
那么這個方法不是有問題么?
 
其實不然,有問題的是我們的寫法
文檔中進行了明確的說明要求
image_5b7bb842_5e68
 
翻譯下:
第一點: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之后
image_5b7bb842_1ad8
image_5b7bb842_328c
 
結果就不再有問題了
 
第二點
combiner 必須和accumulator要兼容
對於任意的u 和 t
image_5b7bb842_5598
這到底是什么意思呢?
 
場景
假設說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))
 
image_5b7bb842_7a6c
 
非並行運算
u 直接與下一個元素進行結合運算

image_5b7bb842_3cb9
 
顯然這只是並行和非並行兩種不同的處理運算方式,他們應該是相同的
也就是
image_5b7bb842_1362
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM