鍍金池/ 問答/Java  C++/ java8中3個參數(shù)的reduce方法怎么理解?

java8中3個參數(shù)的reduce方法怎么理解?

例如這個練習題,使用reduce和lambda表達式來實現(xiàn)map。
不明白的是reduce第三個參數(shù)的意義,感覺多此一舉

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

public class MapUsingReduce {
   
        public static <I, O> List<O> map(Stream<I> stream, Function<I, O> mapper) {
            return stream.reduce(new ArrayList<O>(), (acc, x) -> {
                // We are copying data from acc to new list instance. It is very inefficient,
                // but contract of Stream.reduce method requires that accumulator function does
                // not mutate its arguments.
                // Stream.collect method could be used to implement more efficient mutable reduction,
                // but this exercise asks to use reduce method.
                List<O> newAcc = new ArrayList<>(acc);
                newAcc.add(mapper.apply(x));
                return newAcc;
            }, (List<O> left, List<O> right) -> {
                // We are copying left to new list to avoid mutating it. 
                List<O> newLeft = new ArrayList<>(left);
                newLeft.addAll(right);
                return newLeft;
            });
        }
    
    }

下面是文檔的說明,仍然不明白。。。

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

Performs a reduction on the elements of this stream, using the provided identity, accumulation and combining functions. This is equivalent to:

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

but is not constrained to execute sequentially.

The identity value must be an identity for the combiner function. This means that for all u, combiner(identity, u) is equal to u. Additionally, the combiner function must be compatible with the accumulator function; for all u and t, the following must hold:

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

望指教,謝謝!

回答
編輯回答
朽鹿

我覺得可以這樣理解

首先理解方法本身的意思:
Streamreduce方法,翻譯過來是聚合或者是匯聚成一個的意思,由于Stream本身就代表著一堆數(shù)據(jù),那stream.reduce()方法顧名思義就是把一堆數(shù)據(jù)聚合成一個數(shù)據(jù)

理解了reduce方法的意思,再來看看這個方法掛靠的對象是stream,是一個流,了解一下流的工作方式:
流底層核心其實是Spliterator接口的一個實現(xiàn),而這個Spliterator接口其實本身就是Fork/Join并行框架的一個實現(xiàn),所以歸根結(jié)底要明白流的工作方式,就要明白一下Fork/Join框架的基本思想,即:以遞歸的方式將可以并行的任務拆分成更小的子任務,然后將每個子任務的結(jié)果合并起來生成整體的最后結(jié)果,畫了個草圖如下

clipboard.png

理解了方法本身的意思以及流的工作方式,再結(jié)合到一起理解一下stream.reduce()方法,即用Fork/Join的方式把一堆數(shù)據(jù)聚合成一個數(shù)據(jù),因此可以畫出reduce方法的運行草圖

clipboard.png

結(jié)合草圖,要實現(xiàn)stream.reduce()方法,必須要告訴JDK

  1. 你有什么需求數(shù)據(jù)要匯聚?(Stream已經(jīng)提供了數(shù)據(jù)源,對應上面草圖的A元素)

  2. 最后要匯聚成怎樣的一個數(shù)據(jù)類型(對應reduce方法的參數(shù)一,對應上面草圖的B元素)

  3. 如何將需求數(shù)據(jù)處理或轉(zhuǎn)化成一個匯聚數(shù)據(jù)(對應reduce方法的參數(shù)二,對應上面草圖的匯聚方式1)

  4. 如何將多個匯聚數(shù)據(jù)進行合并(對應reduce方法的參數(shù)三,對應上面草圖的匯聚方式2)

再結(jié)合你給的map方法,其實是要把O類數(shù)據(jù)的流,最后轉(zhuǎn)化為一個I類數(shù)據(jù)的List,因此按照上面的步驟可以進行對照

  1. 你有什么需求數(shù)據(jù)要匯聚?(O類數(shù)據(jù)流)

  2. 最后要匯聚成怎樣的一個數(shù)據(jù)類型(一個集合,new ArrayList()

  3. 如何將需求數(shù)據(jù)處理或轉(zhuǎn)化成一個匯聚數(shù)據(jù)(根據(jù)mapper把O轉(zhuǎn)化為I,再用List.add方法)

  4. 如何將多個匯聚數(shù)據(jù)進行合并(兩個集合合并,用List.addAll()

最后補充一點,若是你的參數(shù)真是Stream<I> streamFunction<I, O> mapper,建議不要用reduce方法,這么寫可能會更好一點

public static <I, O> List<O> map(Stream<I> stream, Function<I, O> mapper) {
        return stream.map(mapper).collect(Collectors.toList());
    }
2017年6月15日 03:14
編輯回答
風畔

樓上說的不錯,但是補充一點:
BinaryOperator是供多線程使用的,如果不在Stream中聲明使用多線程,就不會使用子任務,自然也不會調(diào)用到該方法。另外多線程下使用BinaryOperator的時候是需要考慮線程安全的問題。
另外自問自答下為什么需要BinaryOperator
因為這個重載的方法和其他兩個不相同,允許改變返回值,所以返回值并不一定是Collection的子類;因此必須顯示的聲明如何拼接兩個子任務產(chǎn)生的結(jié)果。
但是java8函數(shù)編程一書中這一題返回的結(jié)果恰好是List,就造成了BinaryOperator并不需要的假象。
個人愚見,如有不對,懇請扶正。

2017年7月28日 21:33