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

reduce-歸約


看下詞典翻譯:
image_5b7bb842_2b7a
好的命名是自解釋的
reduce的方法取得就是其中概括的含義
java8 流相關的操做中,咱們把它理解 "累加器",之因此加引號是由於他並不只僅是加法
他的運算能夠是一個Lambda 表達式
因此更準確的說 reduce 是一個迭代運算器
Stream包的文檔中其實已經說的很明白了
可是就是由於不是很理解因此看的雲裏霧裏
其中說到:
一個reduce操做(也稱爲摺疊)接受一系列的輸入元素,並經過重複應用操做將它們組合成一個簡單的結果
 
參照reduce方法文檔給出的示例

T result = identity; java

for (T element : this stream) 多線程

result = accumulator.apply(result, element) app

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
相關文章
相關標籤/搜索