【问题标题】:Java 8 reduce BinaryOperator what is used for?Java 8 reduce BinaryOperator 有什么用?
【发布时间】:2014-07-21 18:22:17
【问题描述】:

我目前正在阅读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

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

非常感谢任何帮助。

谢谢。

【问题讨论】:

    标签: java java-8 binary-operators


    【解决方案1】:

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

    • 身份
    • 累加器
    • 组合器

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

    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");
    

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

    【讨论】:

    • 感谢 nosid 我有一个问题,为什么我在每次迭代中都会收到不同的结果,我猜是为了并行化......为什么使用名称代码的结果是几个?请查看我编辑的问题。
    • @chiperortiz:我已经更新了关于您的第二个问题的答案。这个例子真的来自书本吗?在这种情况下,good book 这句话似乎有问题。
    • 书中是一个比较复杂的例子,只使用顺序流。
    • 我会使用你的 BiFunction 但我应该使用相同的 BinaryOperator 吗?
    【解决方案2】:

    nosid's answer 基本正确 (+1),但我想强调一个特定点。

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

    看起来这个示例源自 Richard Warburton,Java 8 Lambdas,O'Reilly 2014 的示例 5-19。如果是这样,我将不得不与优秀的 Dr 谈一谈. 沃伯顿。

    【讨论】:

    • 同样,要归约的 BinaryOperator 参数必须是关联的。否则你会得到并行的乱码结果。
    • Richard 的示例用于顺序流而不是并行流 Stuart 感谢您的回复...
    • @chiperortiz 确实这个例子是顺序的,但是代码顺序给出正确的结果和并行给出错误的结果是不合适的,尤其是在试图解释这些东西的书中。 (另外,我怀疑即使是顺序代码也违反了一些限制,幸运的是它恰好给出了正确的结果。)
    猜你喜欢
    • 2018-01-01
    • 1970-01-01
    • 2017-04-23
    • 2014-07-27
    • 2010-09-15
    • 2019-11-12
    • 1970-01-01
    • 2022-12-31
    • 2016-01-08
    相关资源
    最近更新 更多