Java 8函数式编程中'reduce'函数的第三个参数的目的

时间:2014-04-02 10:25:39

标签: java lambda functional-programming java-8

在什么情况下,第三个论点是减少'在Java 8流中调用?

下面的代码尝试遍历字符串列表并添加每个字符串的第一个字符的代码点值。似乎永远不会使用最终lambda返回的值,如果插入println,它似乎永远不会被调用。该文档将其描述为'组合器'但我找不到更多细节......

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

3 个答案:

答案 0 :(得分:18)

您在谈论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;   
     

但不限于按顺序执行。标识值必须是组合器函数的标识。这个   意味着对于所有人来说,组合器(identity,u)等于u。   此外,组合器功能必须与   累加器功能;对于所有你和你,必须具备以下条件:

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

这是终端操作。

     

API注意:使用此表单的许多缩减可以表示得更多   只需通过map和reduce操作的明确组合即可。该   累加器函数充当融合映射器和累加器,它可以   有时比单独的映射和缩减更有效   因为当知道先前减少的值允许你避免一些   计算。类型参数:U - 结果的类型参数:   identity - 组合器函数累加器的标识值 -   用于合并的关联,非干扰,无状态功能   结果组合器中的一个附加元素 - 一个关联的,   用于组合两个值的非干扰无状态函数   必须与累加器函数兼容返回:结果   减少参见:reduce(BinaryOperator),reduce(对象,   BinaryOperator)

我认为它的目的是允许并行计算,因此我的猜测是它仅在并行执行还原时使用。如果按顺序执行,则无需使用combiner。我肯定不知道这一点 - 我只是根据文档评论猜测“[...]不限于按顺序执行”以及其他许多提及“并行执行”的评论。

答案 1 :(得分:9)

我认为来自java.util.stream包摘要的Reduction operations段可以回答这个问题。让我引用这里最重要的部分:


在更一般的形式中,对<T>类型的元素执行reduce操作,产生类型<U>的结果需要三个参数:

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

这里,identity元素既是缩小的初始种子值,也是没有输入元素的默认结果。累加器函数获取部分结果和下一个元素,并产生新的部分结果。组合器功能组合了两个部分结果以产生新的部分结果。 (组合器在并行缩减中是必需的,其中输入被分区,为每个分区计算部分累积,然后组合部分结果以产生最终结果。) 更正式地说,标识值必须是组合器函数的标识。这意味着对于所有ucombiner.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);

虽然显式map-reduce形式更具可读性,因此通常应该是首选。通过将映射和简化组合成单个函数,可以优化大量工作的情况下提供通用形式。


换句话说,据我所知,三参数形式在两种情况下很有用:

  1. 当并行执行很重要时。
  2. 通过组合映射和累积步骤可以实现显着的性能优化。否则,可以使用更简单和可读的显式map-reduce形式。
  3. 以前在同一个文档中提到过显式表单:

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

答案 2 :(得分:4)

确认组合器用法的简单测试代码:

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