Java 将并行阵列流缩减为单个阵列

Java 将并行阵列流缩减为单个阵列,java,multithreading,java-8,java-stream,reduce,Java,Multithreading,Java 8,Java Stream,Reduce,我试图将并行的数组流简化为单个数组列表,以便 我使用累加器和组合器的reduce方法,如下所示:- public static void main(String [] args) { ArrayList<String> l1 = new ArrayList<>(); l1.add("a1"); l1.add("a2"); List<Str

我试图将并行的数组流简化为单个数组列表,以便 我使用累加器和组合器的reduce方法,如下所示:-

    public static void main(String [] args) {
        ArrayList<String> l1 = new ArrayList<>();
        l1.add("a1");
        l1.add("a2");
        
        List<String> l2 = new ArrayList<>();
        l2.add("a3");
        l2.add("a4");
                
        List<List<String>> l = new ArrayList<>();
        l.add(l1);
        l.add(l2);
        
        Stream<List<String>> stream = l.stream();
        join(stream).forEach(System.out::println);
}

private  static  <T> List<T> join(Stream<List<T>> stream) {
        return stream.parallel().reduce(new ArrayList<>(),  (total, element) -> {
            System.out.println("total: " + total);
            System.out.println("element: " + element);
            total.addAll(element);
            return total;
        },  (total1, total2) -> {
            System.out.println("total1: " + total1);
            System.out.println("total2: " + total2);
            total1.addAll(total2);
            return total1;
        });
}
total: []
element: [a3, a4]
total: []
element: [a1, a2]
total1: [a3, a4, a1, a2]
total2: [a3, a4, a1, a2]
a3
a4
a1
a2
a3
a4
a1
a2


那么为什么结果是重复的呢?在累加器中使用数组列表也是线程安全的吗?

您应该只使用
flatMap

返回一个流,该流由将提供的映射函数应用于每个元素而生成的映射流的内容替换该流的每个元素的结果组成。每个映射流在其内容放入该流后关闭。(如果映射流为空,则使用空流。)

这是一个中间操作

代码的问题在于,您将函数式代码与副作用混为一谈。这不是好兆头。如果消除了副作用,则输出与预期一致:

私有静态列表联接(流){
return stream.parallel().reduce(新的ArrayList(),(总计,元素)->{
系统输出打印项次(“总计:+总计);
System.out.println(“元素:”+元素);
//总计。添加全部(元素);
//返回总数;
var列表=新阵列列表(总计);
list.addAll(元素);
退货清单;
},(总计1,总计2)->{
System.out.println(“total1:+total1”);
System.out.println(“total2:+total2”);
//total1.addAll(total2);
//返回总数1;
var列表=新阵列列表(总计1);
list.addAll(总计2个);
退货清单;
});
}

您还应该避免使用
parallel()
,除非您有明确客观的理由这样做。并行性是一种开销,只有当有繁重的工作要做时,它才会变得更高效。否则,同步开销将是比任何收益更大的惩罚。

为什么在并行处理的情况下累加器和合并器应该返回一个新对象?@Abdelrahman_Attya它应该没有副作用。当使用
reduce
时,这些防御副本是不可避免的。相比之下,使用
collect(ArrayList::new,List::addAll,List::addAll)
不仅更短,而且效率更高。总结:reduce设计用于执行不可变的缩减。我们在想要缩减不可变对象流时使用它,例如(int,double,…)但是如果我们想减少一个可变对象,比如(list),我们必须通过每一步返回一个新对象来保证线程安全。
l.stream().flatMap(x -> x.stream()).collect(Collectors.toList()); // is [a1, a2, a3, a4]
l.stream().flatMap(List::stream).collect(Collectors.toList());