2014-04-02 33 views
26

在什麼情況下Java 8流中調用了'reduce'的第三個參數?Java 8函數式編程中'reduce'函數的第三個參數的用途

下面的代碼嘗試遍歷字符串列表並將每個字符的第一個字符的代碼點值相加。最終lambda返回的值似乎永遠不會被使用,如果插入println,它似乎永遠不會被調用。該文檔描述了它作爲一個「組合」,但我不能找到更詳細...

int result = 
    data.stream().reduce(0, (total,s) -> total + s.codePointAt(0), (a,b) -> 1000000); 

回答

17

你說的是this function

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

執行有關此流中的元素的減少,使用所提供的身份,累積和 組合功能。這相當於:

U result = identity; 
for (T element : this stream) 
    result = accumulator.apply(result, element) 
return result; 

但是不限於按順序執行。身份值必須是組合函數的身份。這個 意味着對於所有你,組合器(身份,u)等於u。 此外,組合器功能必須與 累加器功能兼容;對於所有u和t,必須滿足以下條件:

combiner.apply(u, accumulator.apply(identity, t)) == 
    accumulator.apply(u, t) 

這是一個終端操作。

API注意:使用這種形式的許多縮減可以簡單地通過映射和縮小操作的顯式組合來代表更多 。累加器函數用作一個融合映射器和累加器,它可以有效地比單獨的映射和縮減更有效,例如知道先前減少的值時可以避免一些 計算。類型參數:U - 結果類型參數: identity - 組合函數累加器的標識值 - 用於將 附加元素併入結果組合器中的關聯,無干擾,無狀態函數 - 關聯,非關聯, 非-interfering,用於組合兩個值,其 必須與累加器功能兼容無國籍函數返回:結果的降低的 另請參見:減少(BinaryOperator),減少(對象, BinaryOperator)

我假定它的目的是允許並行計算,所以我的猜測是,它只能用於減少pe的情況並行進行。如果按順序執行,則不需要使用combiner。我不知道這是肯定的 - 我只是猜測基於文檔評論「[...]不限制順序執行」以及其他提到的「並行執行」的評論。

+0

那確實功能。並行性上有趣的想法 - 將嘗試... –

+2

這似乎是它。插入「.parallel()」會導致返回1000000。 –

+0

@Garth酷,迫不及待地嘗試一下自己! –

3

簡單測試代碼,確認合成的用法:

String[] strArray = {"abc", "mno", "xyz"}; 
List<String> strList = Arrays.asList(strArray); 

System.out.println("stream test"); 
int streamResult = strList.stream().reduce(
     0, 
     (total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); }, 
     (a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "]"); return 1000000;} 
    ); 
System.out.println("streamResult: " + streamResult); 

System.out.println("parallelStream test"); 
int parallelStreamResult = strList.parallelStream().reduce(
     0, 
     (total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); }, 
     (a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "]"); return 1000000;} 
    ); 
System.out.println("parallelStreamResult: " + parallelStreamResult); 

System.out.println("parallelStream test2"); 
int parallelStreamResult2 = strList.parallelStream().reduce(
     0, 
     (total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); }, 
     (a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "] a+b[" + (a+b) + "]"); return a+b;} 
    ); 
System.out.println("parallelStreamResult2: " + parallelStreamResult2); 

輸出:

stream test 
accumulator: total[0] s[abc] s.codePointAt(0)[97] 
accumulator: total[97] s[mno] s.codePointAt(0)[109] 
accumulator: total[206] s[xyz] s.codePointAt(0)[120] 
streamResult: 326 
parallelStream test 
accumulator: total[0] s[mno] s.codePointAt(0)[109] 
accumulator: total[0] s[abc] s.codePointAt(0)[97] 
accumulator: total[0] s[xyz] s.codePointAt(0)[120] 
combiner: a[109] b[120] 
combiner: a[97] b[1000000] 
parallelStreamResult: 1000000 
parallelStream test2 
accumulator: total[0] s[mno] s.codePointAt(0)[109] 
accumulator: total[0] s[xyz] s.codePointAt(0)[120] 
accumulator: total[0] s[abc] s.codePointAt(0)[97] 
combiner: a[109] b[120] a+b[229] 
combiner: a[97] b[229] a+b[326] 
parallelStreamResult2: 326 
6

我覺得從java.util.stream包摘要Reduction operations款能回答這個問題。讓我引用在這裏最重要的部分:


在其更一般的形式,在類型<T>產生<U>類型的結果的元素減少操作需要三個參數:

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

在這裏,標識元素既是縮減的初始種子值,又是沒有輸入元素時的默認結果。累加器函數獲取部分結果和下一個元素,並生成新的部分結果。組合器功能將兩個部分結果組合以產生新的部分結果。 (組合器在並行減少中是必需的,其中輸入被分區,爲每個分區計算部分累積,然後將部分結果組合以產生最終結果。)更正式地,標識值必須是組合器功能。這意味着對於所有的u,combiner.apply(identity, u)等於u。另外,組合器功能必須是關聯的,並且必須與累加器功能兼容:對於所有的utcombiner.apply(u, accumulator.apply(identity, t))必須是equals()accumulator.apply(u, t)

三參數形式是兩個參數形式的泛化,將映射步驟合併到累加步驟中。使用更一般的形式,如下所示,我們可以重新澆鑄求和的權重的簡單的例子:

int sumOfWeights = widgets.stream() 
          .reduce(0, 
            (sum, b) -> sum + b.getWeight()) 
            Integer::sum); 

雖然明確的地圖,減少形式的可讀性,因此應通常是優選的。通過將映射和減少組合到一個函數中,可以優化大量工作的情況下提供了廣義形式。


換言之,據我明白了,三個參數的形式在兩種情況下是有用的:

  1. 當並行執行的事項。
  2. 當通過組合映射和累積步驟可以實現顯着的性能優化時。否則,可以使用更簡單和可讀的顯式映射縮減表單。

明確的形式在同一文檔前面提到的:

int sumOfWeights = widgets.parallelStream() 
     .filter(b -> b.getColor() == RED) 
     .mapToInt(b -> b.getWeight()) 
     .sum(); 
相關問題