一文搞懂 Java8 reduce操作

張哥說技術發表於2023-12-25

來源:waynblog

什麼是 reduce

Java8 中有兩大最為重要的改變,其一是 Lambda 表示式,另一個就是 Stream API 了。

Stream 是 Java8 中處理集合的關鍵抽象概念,它將資料來源流化後,可以執行非常複雜的查詢、過濾和對映資料、排序、切片、聚合統計等操作。操作之後會產生一個新的流,而資料來源則不會發生改變。

在使用 Stream 操作的過程中,往往有三個步驟,

1. 建立 Stream

從一個資料來源(集合,陣列)中,新建一個 Stream 流。

2. 中間操作

一箇中間操作鏈,對 Stream 流的資料進行處理。比如查詢、過濾、對映轉換等。

3. 終止操作

一個終止操作,執行中間操作鏈,併產生結果。常用的終止操作有 forearch、collect、match、count、min、max、reduce 等。

其中本文主要講解的 reduct 操作就屬於是 Stream 流操作中的終止操作。


reduce 操作是一種通用的歸約操作,它可以實現從 Stream 中生成一個值,其生成的值不是隨意的,而是根據指定的計算模型。

比如終止操作中提到 count、min 和 max 方法,因為常用而被納入標準庫中。事實上這些方法都是一種 reduce 操作。

本文大綱如下,

一文搞懂 Java8 reduce操作

reduce 操作三要素

為了方便大家理解 reduce (規約)操作,先給大家演示一下 reduce 操作的相關程式碼示例,

List<Integer> numbers = Arrays.asList(123456);
int result = numbers
  .stream()
  .reduce(0, (subtotal, element) -> subtotal + element);
assertThat(result).isEqualTo(21);

可以看到,我們的 reduce 操作接受了三個引數,返回了一個 int 基本型別。

在 Stream API 中,提供了三個 reduct 操作方法,根據引數不同進行區分。

一文搞懂 Java8 reduce操作

對應上方程式碼示例,也就是使用了接受兩個引數的 reduce 方法,但其實接受兩個引數的 reduce 方法的程式碼邏輯是和接受三個引數的 reduce 方法是一致的。透過上方截圖可以看出。

所以這裡,我就直接給大家介紹下 reduce 操作的三個引數分別有什麼作用即可。

identiy 引數

identiy(初始值)是 reduce 操作的初始值,也就是當元素集合為空時的預設結果。對應上方程式碼示例,也就是說 reduce 操作的初始值是 0。

accumulator 引數

accumulator(累加器)是一個函式,它接受兩個引數,reduce 操作的部分元素和元素集合中的下一個元素。它返回一個新的部分元素。在這個例子中,累加器是一個 lambda 表示式,它將集合中兩個整數相加並返回一個整數:(a, b) -> a + b。

combiner 引數

combiner(組合器)是一個函式,它用於在 reduce 操作被並行化或者當累加器的引數型別和實現型別不匹配時,將 reduce 操作的部分結果進行組合。在上面程式碼示例中,我們不需要使用組合器,因為上面我們的 reduce 操作不需要並行,而且累加器的引數型別和實現型別都是 Integer。


為了方便大家理解 reduce 操作的內部邏輯,我給大家繪製了上面程式碼示例的執行示意圖,如下,

一文搞懂 Java8 reduce操作

如何使用 reduce 操作

為了更好地理解初始值,累加器和組合器的功能,讓我們看一些基本的例子。

使用 reduce 查詢整數集合的最小值

// 建立一個整數集合
List<Integer> numbers = Arrays.asList(123456);

// 找出集合中的最小值
Integer min = numbers.stream().reduce((integer, integer2) -> {
    if (integer < integer2) {
        return integer;
    } else {
        return integer2;
    }
}).get();

// 輸出結果
System.out.println(min); // 1

在這個例子中,我們使用了一個引數的 reduce 操作,它接受一個累加器函式。累加器函式會返回集合兩個元素中,較小的元素。

最終我們就可以找出集合中最小值 1。

使用 reduce 操作拼接字串列表

我們可以對一個字串列表使用 reduce 操作,將它們拼接成一個單一的字串:

// 建立一個字串列表
List<String> letters = Arrays.asList ("a""b""c""d""e");

// 使用 reduce 操作拼接字串列表
String result = letters
  .stream ()
  .reduce ("", (partialString, element) -> partialString + element);

// 輸出結果
System.out.println (result); // abcde

在這個例子中,我們將初始值設為 "",累加器函式設為 (a, b) -> a + b,它表示將兩個字串拼接起來。

我們可以看到,reduce 操作將累加器函式反覆應用到列表中的每個元素上,得到最終的結果 abcde。

使用並行流計算整數列表的總和

List<Integer> numbers = Arrays.asList(123456);

// 使用並行流和 reduce() 方法計算整數列表的總和
int result = numbers.parallelStream()
        .reduce(0, (a, b) -> a + b, Integer::sum);

// 輸出結果
System.out.println(result); // 21

在這個例子中,我們使用 parallelStream() 方法將列表轉換為並行流,再使用 reduce() 方法對整數列表進行 reduce 操作,並使用 Integer::sum 作為合併函式 combiner,將平行計算的結果合併。

使用並行流的好處能夠充分利用多核 CPU 的優勢,使用多執行緒加快對集合資料的處理速度。

不過並行流也不是任何時候都可以使用的,並行流執行過程中實際按照多執行緒執行,多執行緒程式設計有的問題,並行流都有。

比如多執行緒的執行緒安全,執行順序等問題,並行流都是有的。這一點需要大家注意。

最後聊兩句

本文介紹了 Java8 Stream 流中,reduce 操作的相關概念和接收引數,包含初始值,累加器和組合器,最後介紹了 reduce 操作如何使用,希望大家喜歡。

來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024923/viewspace-3001173/,如需轉載,請註明出處,否則將追究法律責任。

相關文章