java stream api中的reduce方法使用

java stream api中的reduce方法使用

java stream api是對函數式編程的支持,雖然stream api和c# linq比起來就是拖拉機和法拉利的區別,不過勉強能用,總比沒有強。

stream api的reduce方法用於對stream中元素進行聚合求值,最常見的用法就是將stream中一連串的值合成爲單個值,比如爲一個包含一系列數值的數組求和。

reduce方法有三個重載的方法,方法簽名如下

Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

第一個簽名方法接受一個BinaryOperator類型的lambada表達式, 常規應用方法如下

List<Integer> numList = Arrays.asList(1,2,3,4,5);
int result = numList.stream().reduce((a,b) -> a + b ).get();
System.out.println(result);

代碼實現了對numList中的元素累加。lambada表達式的a參數是表達式的執行結果的緩存,也就是表達式這一次的執行結果會被作爲下一次執行的參數,而第二個參數b則是依次爲stream中每個元素。如果表達式是第一次被執行,a則是stream中的第一個元素。

int result = numList.stream().reduce((a,b) -> {
  System.out.println("a=" + a + ",b=" + b);
  return a + b;
} ).get();

在表達式中假如打印參數的代碼,打印出來的內容如下

a=1,b=2
a=3,b=3
a=6,b=4
a=10,b=5

表達式被調用了4次, 第一次a和b分別爲stream的第一和第二個元素,因爲第一次沒有中間結果可以傳遞, 所以 reduce方法實現爲直接將第一個元素作爲中間結果傳遞。

第二個簽名的實現

T reduce(T identity, BinaryOperator<T> accumulator);

與第一個簽名的實現的唯一區別是它首次執行時表達式第一次參數並不是stream的第一個元素,而是通過簽名的第一個參數identity來指定。我們來通過這個簽名對之前的求和代碼進行改進

List<Integer> numList = Arrays.asList(1,2,3,4,5);
int result = numList.stream().reduce(0,(a,b) ->  a + b );
System.out.println(result);

其實這兩種實現幾乎差別,第一種比第一種僅僅多了一個字定義初始值罷了。 此外,因爲存在stream爲空的情況,所以第一種實現並不直接方法計算的結果,而是將計算結果用Optional來包裝,我們可以通過它的get方法獲得一個Integer類型的結果,而Integer允許null。第二種實現因爲允許指定初始值,因此即使stream爲空,也不會出現返回結果爲null的情況,當stream爲空,reduce爲直接把初始值返回。

第三種簽名的用法相較前兩種稍顯複雜,猶豫前兩種實現有一個缺陷,它們的計算結果必須和stream中的元素類型相同,如上面的代碼示例,stream中的類型爲int,那麼計算結果也必須爲int,這導致了靈活性的不足,甚至無法完成某些任務, 比入我們咬對一個一系列int值求和,但是求和的結果用一個int類型已經放不下,必須升級爲long類型,此實第三簽名就能發揮價值了,它不將執行結果與stream中元素的類型綁死。

List<Integer> numList = Arrays.asList(Integer.MAX_VALUE,Integer.MAX_VALUE);
long result = numList.stream().reduce(0L,(a,b) ->  a + b, (a,b)-> 0L );
System.out.println(result);

如上代碼所示,它能見int類型的列表合併成long類型的結果。
當然這只是其中一種應用罷了,猶豫拜託了類型的限制我們還可以通過他來靈活的完成許多任務,比入將一個int類型的ArrayList轉換成一個String類型的ArrayList

List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6);
ArrayList<String> result = numList.stream().reduce(new ArrayList<String>(), (a, b) -> {
    a.add("element-" + Integer.toString(b));
    return a;
}, (a, b) -> null);
System.out.println(result);

執行結果爲

[element-1, element-2, element-3, element-4, element-5, element-6]

這個示例顯得有點雞肋,一點不實用,不過在這裏我們的主要目的是說明代碼能達到什麼樣的效果,因此代碼示例也不必取自實際的應用場景。

從上面兩個示例可以看出第三個reduce比前面兩個強大的多,它的功能已經完全覆蓋前面兩個的實現,如果我們不考慮代碼的簡潔性,甚至可以拋棄前面兩個。

另外,還需要注意的是這個reduce的簽名還包含第三個參數,一個BinaryOperator<U>類型的表達式。在常規情況下我們可以忽略這個參數,敷衍了事的隨便指定一個表達式即可,目的是爲了通過編譯器的檢查,因爲在常規的stream中它並不會被執行到,然而, 雖然此表達式形同虛設,可是我們也不是把它設置爲null,否者還是會報錯。 在並行stream中,此表達式則會被執行到,在這裏我們不進行講解,因爲我自己也沒用過。

numList.parallelStream()

可獲得並行stream

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章