Java8 歸約 reduce

Java8 歸約 reduce

本節將看到如何把一個流中的元素組合起來,使用reduce操作來表達更復雜的查詢,比如“計算菜單中的總的卡路里”或“菜單中卡路里最高的菜時哪一個”。此類查詢需要將流中所有元素反覆結合起來,得到一個值,比如一個Integer。這樣的查詢可以被歸類爲歸約操作(將流歸約成一個值)。用函數式編程語言的術語來說,這稱爲摺疊(fold),因爲你可以將一個操作看成一張長長的紙(你的流)反覆摺疊成一個小方塊,而這就是摺疊操作的結果。

元素求和

在我們研究如何使用reduce方法之前,先來看看如何使用for-each循環來對數字列表中的元素求和:

Int sum = 0;

for (int x : numbers)

Sum += x;

numbers中的每一個元素都用加法運算符反覆迭代來得到結果。通過反覆使用加法,你把一個數字列表歸約成了一個數字。這段代碼中有2個參數:

1.總和變量的初始值,在這裏是0;

2.將列表中所有元素結合在一起的操作,在這裏是+。

要是還能把所有的數字相乘,而不必去複製粘貼這段代碼,豈不是很好?這正是reduce操作的用武之地,它對這種重複應用的模式做了抽象。你可以像下面這樣對流中所有的元素求和:

Int sum = numbers.stream().reduce(0, (a,b) -> a+b);

reduce 接受2個參數:

  1. 一個初始值,這裏是0:
  2. 一個BinaryOperator<T>來將兩個元素結合起來產生一個新值,這裏我們用的是lambda(a, b) -> a+b。

你也很容易把所有的元素相乘,只需要將另一個Lambda:(a, b) -> a*b

傳遞給reduce操作就可以了:

int product = numbers.stream().reduce(1, (a,b) -> a*b);

下圖展示了reduce操作時如何作用於一個流的:Lambda反覆結合每個元素,直到流被歸約成一個值。

 

讓我們深入研究一下reduce操作時如何對一個數字流求和的。首先,0作爲Lambda(a)的第一個參數,從流中獲取4作爲第二個參數(b)。0+4得到4,它成了新的累積值。然後再用累積值和流中下一個元素5調用Lambda,產生新的累積值9。接下來,再用累積值和下一個元素3調用Lambda,得到12。最後,用12和流中最後一個元素9調用Lambda,得到最終結果12。

你可以使用方法引用讓着段代碼更簡潔。在java8中,Integer類現在有了一個靜態的sum方法來對兩個數求和,這恰好時我們想要的,用不着反覆用Lambda寫同一段代碼了:

int sum = numbers.stream().reduce(Integer::sum);

無初始值

reduce 還有一個重載的變體,它不接受初始值,但是會返回一個Optional對象:

Optional<Integer> sum = numbers.stream().reduce((a,b) -> a+b);

爲什麼它返回一個Optional<Integer>呢?考慮流中沒有任何元素的情況。reduce操作無法返回其和,因爲它沒有初始值。這就是爲什麼結果被包裹在一個Optional對象裏,以表明和可能不存在。現在看看reduce還能做什麼。

如果對於有無初始值不太明白,可以查看源碼:

這是有初始值的源碼:

 

這是沒有初始值的源碼:

 

參考書籍:Java8實戰 

發佈了431 篇原創文章 · 獲贊 80 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章