Java 8减少BinaryOperator用于什么?

时间:2014-06-01 14:29:59

标签: java java-8 binary-operators

我目前正在阅读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然后返回N.P.E,则输出相同。

该参数用于什么?

更新

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

首次运行。

returned StringBuilder length = 420

第二次运行

returned StringBuilder length = 546

第三次运行

returned StringBuilder length = 348

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

任何帮助都非常感激。

感谢。

2 个答案:

答案 0 :(得分:14)

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

  • 身份
  • 累加器
  • 合成

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

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

以下是combiner如何用于将顺序缩减转换为缩减的示例,该缩减支持并行执行。有一个包含四个String s的流,acc用作accumulator.apply的缩写。然后,减少的结果可以如下计算:

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

使用兼容的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);

答案 1 :(得分:3)

nosid's answer大部分都是正确的(+1),但我想扩大一个特定点。

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

看起来这个例子起源于Richard Warburton的例子5-19, Java 8 Lambdas ,O'Reilly 2014.如果是这样,我将不得不与好博士谈谈这个问题。沃伯顿。