Java Stream 减少无法解释的行为

谁能指出我正确的方向,因为我无法理解这个问题。


我正在执行以下方法。


private static void reduce_parallelStream() {

    List<String> vals = Arrays.asList("a", "b");


    List<String> join = vals.parallelStream().reduce(new ArrayList<String>(),

            (List<String> l, String v) -> {


                l.add(v);


                return l;

            }, (a, b) -> {                   

                a.addAll(b);

                return a;

            }


    );


   System.out.println(join);


}

它打印


[空,a,空,a]


我不明白为什么它在结果列表中放置两个空值。我预计答案是


[一、二]


因为它是并行流,所以要减少的第一个参数


新的数组列表()


对于每个输入值 a 和 b 可能会被调用两次。


然后,累加器函数可能会被调用两次,因为它是一个并行流,并在每次调用中传递每个输入“a 和 b”以及种子值提供的列表。因此,a 被添加到列表 1,b 被添加到列表 2(反之亦然)。之后组合器将组合两个列表,但它不会发生。


有趣的是,如果我在累加器中放置一条打印语句来打印输入的值,则输出会发生变化。所以以下


private static void reduce_parallelStream() {

    List<String> vals = Arrays.asList("a", "b");


    List<String> join = vals.parallelStream().reduce(new ArrayList<String>(),

            (List<String> l, String v) -> {

                System.out.printf("l is %s", l);

                l.add(v);

                System.out.printf("l is %s", l);

                return l;

            }, (a, b) -> {

                a.addAll(b);

                return a;

            }


    );


   System.out.println(join);


}

结果是这个输出


l 是 []l 是 [b]l 是 [b, a]l 是 [b, a] [b, a, b, a]


谁能解释一下。


温温酱
浏览 113回答 3
3回答

蝴蝶刀刀

Collections.synchronizedList()您在使用时应该使用parallelStream(). 因为ArrayList它不是线程安全的,并且在并发访问它时会出现意外的行为,就像您使用parallelStream().我已经修改了您的代码,现在它可以正常工作:private static void reduce_parallelStream() {&nbsp; &nbsp; List<String> vals = Arrays.asList("a", "b");&nbsp; &nbsp; // Use Synchronized List when with parallelStream()&nbsp; &nbsp; List<String> join = vals.parallelStream().reduce(Collections.synchronizedList(new ArrayList<>()),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (l, v) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l.add(v);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return l;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }, (a, b) -> a // don't use addAll() here to multiplicate the output like [a, b, a, b]&nbsp; &nbsp; );&nbsp; &nbsp; System.out.println(join);}输出:有时你会得到这样的输出:[a, b]有时还有这个:[b, a]这样做的原因是它是一个parallelStream()所以你无法确定执行的顺序。

HUX布斯

因为它是并行流,所以要减少的第一个参数new ArrayList() 可能会为每个输入值 a 和 b 调用两次。这就是你错的地方。第一个参数是单个ArrayList实例,而不是lambda 表达式可以产生多个ArrayList实例。因此,整个缩减操作在单个ArrayList实例上进行。当多个线程ArrayList并行修改该值时,每次执行的结果可能会发生变化。您combiner实际上将 a 的所有元素添加List到同一个List.[a,b]如果 和accumulator函数combiner都会生成新的ArrayList而不是改变其输入,则可以获得预期的输出ArrayList:List<String> join = vals.parallelStream().reduce(&nbsp; &nbsp; &nbsp;new ArrayList<String>(),&nbsp; &nbsp; &nbsp; &nbsp; (List<String> l, String v) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; List<String> cl = new ArrayList<>(l);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cl.add(v);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return cl;&nbsp; &nbsp; &nbsp; &nbsp; }, (a, b) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; List<String> ca = new ArrayList<>(a);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ca.addAll(b);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ca;&nbsp; &nbsp; &nbsp; &nbsp; });也就是说,您reduce根本不应该使用。collect是执行可变归约的正确方法:List<String> join = vals.parallelStream()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .collect(ArrayList::new,ArrayList::add,ArrayList::addAll);正如您所看到的,这里与 in 不同reduce,您传递的第一个参数是 a Supplier<ArrayList<String>>,它可用于生成所需数量的中间ArrayList实例。

RISEBY

这很简单,第一个参数是身份,或者我会说从零开始。因为parallelStream usage这个值被重用。这意味着并发问题(添加中的空值)和重复问题。这可以通过以下方式修补:&nbsp; &nbsp; final ArrayList<String> zero = new ArrayList<>();&nbsp; &nbsp; List<String> join = vals.parallelStream().reduce(zero,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (List<String> l, String v) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (l == zero) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l = new ArrayList<>();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l.add(v);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return l;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }, (a, b) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // See comment of Holger:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (a == zero) return b;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (b == zero) return a;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a.addAll(b);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return a;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; );安全的。您可能想知道为什么reduce身份提供函数没有重载。原因是collect这里应该使用它。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java