上述的demo中發現reduce和collect的作用幾乎一樣,都是返回一個最終的結果,比如,我們可以使用reduce實現toList效果: //手動實現toListCollector --- 濫用reduce, 不可變的規約---不可以並行 List<Integer> calories = dishes.stream().map(Dish::getCalories) .reduce(new ArrayList<Integer>(), (List<Integer> l, Integer e) -> { l.add(e); return l; }, (List<Integer> l1, List<Integer> l2) -> { l1.addAll(l2); return l1; } );
關於上述做法解釋一下。 <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
U是返回值類型,這里就是List BiFunction<U, ? super T, U> accumulator是是累加器,目標在於累加值和單個元素的計算規則。這里就是List和元素做運算,最終返回List。即,添加一個元素到list。 BinaryOperator<U> combiner是組合器,目標在於把兩個返回值類型的變量合並成一個。這里就是兩個list合並。 這個解決方案有兩個問題:一個是語義問題,一個是實際問題。語義問題在於,reduce方法旨在把兩個值結合起來生成一個新值,它是一個不可變歸約。相反,collect方法的設計就是要改變容器,從而累積要輸出的結果。這意味着,上面的代碼片段是在
濫用reduce方法,因為它在原地改變了作為累加器的List。錯誤的語義來使用reduce方法還會造成一個實際問題:這個歸約不能並行工作,因為由多個線程並發修改同一個數據結構可能會破壞List本身。在這種情況下,如果你想要線程安全,就需要每次分
配一個新的List,而對象分配又會影響性能。這就是collect適合表達可變容器上的歸約的原因,更關鍵的是它適合並行操作。 總結:reduce適合不可變容器歸約,collect適合可變容器歸約。collect適合並行。