Java 8 流并行减少 BiFunction 累加器

Posted

技术标签:

【中文标题】Java 8 流并行减少 BiFunction 累加器【英文标题】:Java 8 stream parallel reduce BiFunction accumulator 【发布时间】:2014-12-16 01:58:13 【问题描述】:

我正在学习 Java 8。我要面对的最困难的事情是 Parallel Reduction。这是我正在研究的用户@Stuart Marks 的示例代码。

class ImmutableAverager 

    private final int total;
    private final int count;
    public ImmutableAverager()this.total = 0;this.count = 0;
   public ImmutableAverager(int total, int count)
   
      this.total = total;
      this.count = count;
   
   public double average()return count > 0 ? ((double) total) / count : 0;
   public ImmutableAverager accept(final int i)
   
       return new ImmutableAverager(total + i, count + 1);
   
   public ImmutableAverager combine(final ImmutableAverager other)
   
       return new ImmutableAverager(total + other.total, count + other.count);
     

电话

public static void main(String[] args)     

       System.out.println(Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
        .parallel()
        .reduce(new ImmutableAverager(), 
                ImmutableAverager::accept,
                ImmutableAverager::combine)
        .average());    

这会产生正确的结果,但后来我检查了 reduce 方法的签名

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

如果代码是这样的,我会清楚地理解:

.reduce(new ImmutableAverager(),(a,b)->a.accept(b),(a,b)->a.combine(b))

我不明白怎么做:

 ImmutableAverager::accept

可以转换成BiFunction

我的理解是这样的:

ImmutableAverager::accept

将其转换为类似

(ImmutableAverage a)->a.accept(); //but this is a function with 1 parameter not with 2 parameters.

ImmutableAverager::merge

可以转换成BinaryOperator。我的朋友@Stuart Marks 说

方法匹配函数参数以减少,因此我们可以使用方法引用。

【问题讨论】:

嗨。幸运的是,当您问这个问题时,我恰好在线。 :-) 【参考方案1】:

是的,当使用这种方法引用,特别是“未绑定”方法引用时,参数的转移方式有一个微妙之处。

让我们看看reduce() 的第二个参数。它想要

BiFunction<U, ? super T, U> accumulator

所以它的抽象方法的签名是:

U apply(U, T)

(为简洁起见省略了通配符)。该示例使用了方法引用ImmutableAverager::accept,其签名为:

ImmutableAverager accept(int i)

看起来这样不起作用,因为BiFunction 需要两个参数,而accept 方法只需要一个。但请注意,accept 方法是ImmutableAverager 类上的一个实例方法,因此它隐含地接受了一个“接收者”,即调用此方法的对象。对该方法的普通调用可能如下所示:

newAverager = oldAverager.accept(i);

真的,accept 方法实际上接受了 两个 参数,尽管它看起来并不像。第一个是接收器,类型为ImmutableAverager,第二个是类型int。方法调用语法使接收者看起来有些特殊,但实际上并没有。就好像这是一个像这样调用的静态方法:

newAverager = accept(oldAverager, i);

现在让我们看看reduce 调用是如何工作的。有问题的代码是,

reduce(..., ImmutableAverager::accept, ...)

我在这里只展示第二个参数。这需要是一个BiFunction,它接受UT 的参数并返回一个U,如上所示。如果您查看accept 方法并将接收者视为普通参数而不是特殊的东西,它接受ImmutableAverager 类型的参数和int 类型的参数,并返回ImmutableAverager。所以U 被推断为ImmutableAveragerT 被推断为Integer(从int 装箱),这里的方法参考有效。

关键是,对于一个未绑定的方法引用,方法引用是一个实例方法,但是该方法是使用类名而不是一个实际的实例来指定的.发生这种情况时,接收者会变成方法调用的第一个参数。

【讨论】:

以上是关于Java 8 流并行减少 BiFunction 累加器的主要内容,如果未能解决你的问题,请参考以下文章

java 8流和并行流之间的区别

Java 8 并行流中的自定义线程池

RecursiveTask和RecursiveAction的使用 以及java 8 并行流和顺序流(转)

Java8新特性及实战视频教程完整版

将数组的并行流减少为单个数组

Java 8新特性之 并行和并行数组(八恶人-8)