java8的reduce为啥在没有使用并行流时也要输入第三个参数

搬运一个过来:
函数式编程 - java8中3个参数的reduce方法怎么理解? - SegmentFault

■网友
当然是因为没有其他的(逃
分成几步来解释把。当返回类型相同的时候为啥没有combiner,因为没有必要,即使在并行流里直接使用accumulator本身合并结果就行。那么为什么返回类型不同时需要combiner,因为即是不是并行流也有可能需要combiner,一个流的实现完全可以是先用当前的U和接下来的两个T调用两次accumulator拿到两个U,然后再用combiner合并成一个,然后下一次迭代时再用那个。或者先用identity调用accumulator把所有的T给map成U,然后再reduce出结果。
总体来说只存在3个参数且返回结果类型不同的reduce并不是因为不能有2个参数的,单纯是因为这样更合理而已。reduce的定义本身并不考虑其顺序,只考虑不管什么顺序都能拿到最终结果

■网友
占坑明天答!


直接看源码:
public final \u0026lt;R, A\u0026gt; R collect(Collector\u0026lt;? super P_OUT, A, R\u0026gt; collector) {
A container;
if (isParallel()
\u0026amp;\u0026amp; (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
\u0026amp;\u0026amp; (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
container = collector.supplier().get();
BiConsumer\u0026lt;A, ? super P_OUT\u0026gt; accumulator = collector.accumulator();
forEach(u -\u0026gt; accumulator.accept(container, u));
}
else {
container = evaluate(ReduceOps.makeRef(collector));
}
return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
? (R) container
: collector.finisher().apply(container);
}
设置IDENTITY_FINISH属性,表示不调用finisher函数,直接强制转换A-\u0026gt;R。
设置CONCURRENT属性,表示使用同一个容器,即使是并行流parallelStream也是如此,因为只使用一个容器,所以不会调用combiner函数,combiner函数只会到没有设置CONCURRENT属性并且是并行流的时候才会调用。不信,你把Combiner函数设置一个null就可以证明,和什么乱七八糟的返回类型相同或者不同没有关系的。


【java8的reduce为啥在没有使用并行流时也要输入第三个参数】 附带上测试代码:
public class CollectorCharacteristics\u0026lt;T\u0026gt; implements Collector\u0026lt;T, Set\u0026lt;T\u0026gt;, Map\u0026lt;T, T\u0026gt;\u0026gt;{ @Override public Supplier\u0026lt;Set\u0026lt;T\u0026gt;\u0026gt; supplier() { return () -\u0026gt; { // 如果是并行流且没有设置CONCURRENT属性,则会创建多个容器 // 如果是并行流并且设置了CONCURRENT属性 或者 不是并行流 则只会创建一个容器 System.out.println( "----验证并行流\u0026amp;CONCURRENT 属性之间的关系----" ); return new HashSet\u0026lt;\u0026gt;(); }; } @Override public BiConsumer\u0026lt;Set\u0026lt;T\u0026gt;, T\u0026gt; accumulator() { return ( set, e ) -\u0026gt; { // 如果设置了CONCURRENT属性,则在accumulator一定不能出现使用set的场景,除了set.add(e) // 比如下面的打印set,由于设置了CONCURRENT属性,因此在遍历的时候,别的线程可能正在添加元素,这样就会出现并发修改异常 //System.out.println( "acc:" + set + "," + Thread.currentThread().getName() ); //System.out.println( "acc:" + "," + Thread.currentThread().getName() ); set.add( e ); }; } @Override public BinaryOperator\u0026lt;Set\u0026lt;T\u0026gt;\u0026gt; combiner() { return ( set1, set2 ) -\u0026gt; { System.out.println( "!!! combiner is called when parallelStream \u0026amp; NOT set CONCURRENT !!!\" + Thread.currentThread().getName() ); System.out.println( "set1:" + set1 ); System.out.println( "set2:" + set2 ); set1.addAll( set2 ); return set1; }; } @Override public Function\u0026lt;Set\u0026lt;T\u0026gt;, Map\u0026lt;T, T\u0026gt;\u0026gt; finisher() { return ( x ) -\u0026gt; { Map\u0026lt;T, T\u0026gt; map = new HashMap\u0026lt;\u0026gt;(); x.forEach( ( e ) -\u0026gt; map.put( e, e ) ); return map; }; } @Override public Set\u0026lt;Characteristics\u0026gt; characteristics() { // 如果finisher函数不满足identity,则不能设置IDENTITY_FINISH // 设置IDENTITY_FINISH属性,表示不调用finisher函数,直接强制转换A-\u0026gt;R // 设置CONCURRENT属性,表示使用同一个容器,即使是并行流parallelStream也是如此,因为只使用一个容器,所以不会调用combiner函数 return Collections.unmodifiableSet( EnumSet.of( UNORDERED ) ); } public static void main( String args ) { Map\u0026lt;String, String\u0026gt; map = null; List\u0026lt;String\u0026gt; strs = Arrays.asList( "hello", "world", "welcome", "a", "b", "c", "b" ); System.out.println( Runtime.getRuntime().availableProcessors() ); for( int i = 0; i \u0026lt; 100; ++i ) { //map = strs.stream().collect( new CollectorCharacteristics\u0026lt;\u0026gt;() ); map = strs.parallelStream().collect( new CollectorCharacteristics\u0026lt;\u0026gt;() ); System.out.println( map ); } System.out.println( "===================" ); System.out.println( map ); }}


推荐阅读