6

我目前正在阅读O'reilly Java 8 Lambdas是一本非常好的书。我遇到了一个这样的例子。

我有一个

private final BiFunction<StringBuilder,String,StringBuilder>accumulator=
(builder,name)->{if(builder.length()>0)builder.append(",");builder.append("Mister:").append(name);return builder;};

final Stream<String>stringStream = Stream.of("John Lennon","Paul Mccartney"
,"George Harrison","Ringo Starr");
final StringBuilder reduce = stringStream
    .filter(a->a!=null)
    .reduce(new StringBuilder(),accumulator,(left,right)->left.append(right));
 System.out.println(reduce);
 System.out.println(reduce.length());

这会产生正确的输出。

Mister:John Lennon,Mister:Paul Mccartney,Mister:George Harrison,Mister:Ringo Starr

我的问题被认为是reduce方法的最后一个参数,它是BinaryOperator

我的问题是这个参数是用来做什么的?如果我改变

.reduce(new StringBuilder(),accumulator,(left,right)->new StringBuilder());

如果我通过则输出相同,NULL则返回 NPE。

这个参数是做什么用的?

更新

为什么如果我运行它parallelStream我会收到不同的结果?

首轮。

returned StringBuilder length = 420

第二轮

returned StringBuilder length = 546

第三轮

returned StringBuilder length = 348

等等?这是为什么...不应该在每次迭代时返回所有值?

任何帮助都非常感谢。

谢谢。

4

2 回答 2

14

reduce接口中的方法Stream被重载。具有三个参数的方法的参数是:

  • 身份
  • 累加器
  • 合路器

combiner支持并行执行。显然,它不用于顺序流。但是,没有这样的保证。如果您将更改为并行流,我想您会看到不同:

Stream<String>stringStream = Stream.of(
    "John Lennon", "Paul Mccartney", "George Harrison", "Ringo Starr")
    .parallel();

这是一个示例,说明如何combiner使用 将顺序归约转换为支持并行执行的归约。有一个带有四个Strings的流,acc用作 的缩写accumulator.apply。那么归约的结果可以计算如下:

acc(acc(acc(acc(identity, "one"), "two"), "three"), "four");

使用 compatible combiner,上面的表达式可以转化为下面的表达式。现在可以在不同的线程中执行这两个子表达式。

combiner.apply(
    acc(acc(identity, "one"), "two"),
    acc(acc(identity, "three"), "four"));

关于你的第二个问题,我用一个简化accumulator的来解释问题:

BiFunction<StringBuilder,String,StringBuilder> accumulator =
    (builder,name) -> builder.append(name);

根据Stream::reduce的 Javadoc ,accumulator必须是associative。在这种情况下,这意味着以下两个表达式返回相同的结果:

acc(acc(acc(identity, "one"), "two"), "three")  
acc(acc(identity, "one"), acc(acc(identity, "two"), "three"))

上述情况并非如此accumulator。问题是,您正在改变identity. 这对reduce手术来说是个坏主意。以下是两个应该可行的替代实现:

// identity = ""
BiFunction<String,String,String> accumulator = String::concat;

// identity = null
BiFunction<StringBuilder,String,StringBuilder> accumulator =
    (builder,name) -> builder == null
        ? new StringBulder(name) : builder.append(name);
于 2014-06-01T14:44:17.110 回答
3

nosid 的回答基本正确(+1),但我想放大一个特定点。

identity参数 toreduce必须是一个标识。如果它是一个对象没关系,但如果是,它应该是不可变的。如果“身份”对象发生了变异,它就不再是身份了!有关这一点的更多讨论,请参阅对相关问题的回答。

看起来这个示例源自 Richard Warburton 的示例 5-19,Java 8 Lambdas,O'Reilly 2014。如果是这样,我将不得不与优秀的 Warburton 博士谈谈这个问题。

于 2014-06-01T22:55:00.303 回答