【问题标题】:Third param Java8 reduce on string attribute in Object第三个参数Java8减少对象中的字符串属性
【发布时间】:2018-05-12 12:03:44
【问题描述】:

Stream Guru 的问题很简单:

我有这个:

    List<MyBean> beans = new ArrayList<>();
    beans.add(new MyBean("tutu"));
    beans.add(new MyBean("toto"));
    beans.add(new MyBean("titi"));

比较:

 StringBuilder reduced
            = beans.parallelStream()
                    .map(MyBean::getName)
                    .reduce(new StringBuilder(), (builder, name) -> {
                        if (builder.length() > 0) {
                            builder.append(", ");
                        }

                        builder.append(name);
                        return builder;
                    }, (left, right) -> left.append(right));

 StringBuilder reduced
            = beans.parallelStream()
                    .map(MyBean::getName)
                    .reduce(new StringBuilder(), (builder, name) -> {
                        if (builder.length() > 0) {
                            builder.append(", ");
                        }

                        builder.append(name);
                        return builder;
                    }/* WITHOUT THIRD PARAM*/);

为什么,第二个解决方案没有编译...第三个参数用于并行流...

您能解释一下为什么我无法编译第二个代码部分吗?

【问题讨论】:

    标签: java lambda java-8 java-stream reduce


    【解决方案1】:

    这种行为是因为reduce的这种重载:

    reduce(T identity, BinaryOperator<T> accumulator)
    

    接受两个参数,第二个是BinaryOperator&lt;T&gt;,它基本上表示对两个相同类型的操作数的操作,产生与操作数相同类型的结果.在您的第二个代码 sn-p 中没有遵守此约定,因为您的 map 操作返回 Stream&lt;String&gt;identity 的类型为 StringBuilder。为防止编译器错误,只需将 identity 值和 accumulator 函数的操作数设为相同类型,即:

    StringBuilder reduced
                    = beans.stream()
                    .map(b -> new StringBuilder(b.getName()))
                    .reduce(new StringBuilder(), (builder, name) -> {
                        if (builder.length() > 0) {
                            builder.append(", ");
                        }
    
                        builder.append(name);
                        return builder;
                    }/* WITHOUT THIRD PARAM*/);
    

    另一方面,reduce 的重载:

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

    接受BiFunction 作为第二个参数,它可以使用两种不同类型的对象,因此您的第一个代码 sn-p 可以在没有编译器错误的情况下工作。

    最后,不要忘记将beans.parallelStream() 更改为beans.stream(),如上述解决方案所示。

    顺便说一句,请注意,在并行执行操作时,累加器以及组合器必须associativenon-interferingstateless。如果不考虑这一点,您的结果将是非确定性任意结果。

    编辑:

    Holger所述:

    修改归约函数中的传入参数被 定义,即使它碰巧产生了预期的结果 顺序上下文。也违反了第一个合同 参数,因为修改后的 StringBuilder 不是标识值 了。您可以将 Reduction 与不可变值一起使用,如 .map(MyBean::getName).reduce((a,b) -&gt; a + ", " + b).orElse(""); 或使用 可变约简 .map(MyBean::getName).collect(Collectors.joining(", "));

    【讨论】:

    • 这不仅仅是一个旁注。修改归约函数中的传入参数被定义破坏,即使它碰巧在顺序上下文中产生预期的结果。它也违反了第一个参数的约定,因为修改后的StringBuilder 不再是一个身份值。您可以使用具有不可变值的归约,如 .map(MyBean::getName).reduce((a,b) -&gt; a + ", " + b).orElse("") 或使用可变归约,如 .map(MyBean::getName).collect(Collectors.joining(", "))...
    • @Holger 是的,这完全有道理。我将在帖子中使用您的建议。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2016-10-12
    • 1970-01-01
    • 2022-01-01
    • 2021-10-24
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    相关资源
    最近更新 更多