2014-04-02 34 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(); 
相关问题