java streams减少操作的意外副作用

java streams减少操作的意外副作用,java,java-8,java-stream,side-effects,Java,Java 8,Java Stream,Side Effects,在流上调用“reduce”时,我观察到了底层集合的副作用。这是很基本的。我不敢相信我所看到的,但我找不到错误。下面是代码和结果输出。有人能向我解释为什么流的一个底层集合正在变异吗 package top; import java.util.ArrayList; import java.util.Arrays; import java.util.stream.Stream; public class Main { public static void main(String[] arg

在流上调用“reduce”时,我观察到了底层集合的副作用。这是很基本的。我不敢相信我所看到的,但我找不到错误。下面是代码和结果输出。有人能向我解释为什么流的一个底层集合正在变异吗

package top;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();
        ArrayList<Integer> list3 = new ArrayList<>();

        list1.addAll(Arrays.asList(1, 2, 3));
        list2.addAll(Arrays.asList(4, 5, 6));
        list3.addAll(Arrays.asList(7, 8, 9));

        System.out.println("Before");
        System.out.println(list1);
        System.out.println(list2);
        System.out.println(list3);

        ArrayList<Integer> r1 = Stream.of(list1, list2, list3).reduce((l, r) -> {
            l.addAll(r);
            return l;
        }).orElse(new ArrayList<>());

        System.out.println("After");
        System.out.println(list1);
        System.out.println(list2);
        System.out.println(list3);

        System.out.println("Result");
        System.out.println(r1);

    }
}

// Output
Before
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
After
[1, 2, 3, 4, 5, 6, 7, 8, 9] // Why is this not [1,2,3]?
[4, 5, 6]
[7, 8, 9]
Result
[1, 2, 3, 4, 5, 6, 7, 8, 9]

包顶;
导入java.util.ArrayList;
导入java.util.array;
导入java.util.stream.stream;
公共班机{
公共静态void main(字符串[]args)引发异常{
ArrayList list1=新的ArrayList();
ArrayList list2=新的ArrayList();
ArrayList list3=新的ArrayList();
list1.addAll(Arrays.asList(1,2,3));
list2.addAll(Arrays.asList(4,5,6));
addAll(Arrays.asList(7,8,9));
系统输出打印项次(“之前”);
System.out.println(列表1);
System.out.println(列表2);
System.out.println(列表3);
ArrayList r1=Stream.of(list1,list2,list3).reduce((l,r)->{
l、 addAll(r);
返回l;
}).orElse(新ArrayList());
System.out.println(“之后”);
System.out.println(列表1);
System.out.println(列表2);
System.out.println(列表3);
系统输出打印项次(“结果”);
系统输出打印项次(r1);
}
}
//输出
之前
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
之后
[1,2,3,4,5,6,7,8,9]//为什么这不是[1,2,3]?
[4, 5, 6]
[7, 8, 9]
后果
[1, 2, 3, 4, 5, 6, 7, 8, 9]
获取(可能是任意的?)对元素,并将累加器应用于它们。这不是给定累加器所需的,因为
ArrayList.addAll
会改变列表

解决方案是在累加器中创建一个新列表,并将参数附加到该列表中,或者使用
流.collect
函数与您自己的
收集器或
收集器一起使用。toCollection(ArrayList::new)

这也意味着永远不需要
.orElse(new ArrayList())
,因为在这种情况下将返回标识


请注意,从技术上讲,您的函数不是,因此,如果列表不按顺序运行,它可能不会总是以相同的顺序结束。

您可以使用另一个重载的
Stream.reduce(t identity,BinaryOperator acculator)
,它允许对累加器进行种子设定,即

ArrayList<Integer> r1 = Stream.of(list1, list2, list3)
   .reduce(new ArrayList<>(), (l, r) -> {l.addAll(r); return l;})
ArrayList r1=Stream.of(列表1、列表2、列表3)
.reduce(新的ArrayList(),(l,r)->{l.addAll(r);返回l;})

在您的情况下,您不为累加器提供种子,因此使用第一个可用值。

Crap。我想我已经意识到这正是Java中的工作方式。是这样吗?真的吗?你认为l.addAll(r)
做什么?它更新
l
列表的内容。那么,为什么您对列表被更新感到困惑呢?是的,您明确地告诉您的代码这样做:使用
l.addAll(r);返回l告诉您将所有内容添加到左侧列表,然后返回左侧列表。对于每个迭代,左边的列表是
list1
。我认为Brian的回答已经足够解释了。使用另一种形式不会否定简化操作是无状态的需要。如果流是以并行模式执行的,那么每个线程都会独立地使用单个标识“值”,从而造成彻底的破坏。你不能这样做,所以你建议的解决方案是有缺陷的。好吧,现在应该是正确的。函数与结果相关,只是无法预测哪些列表会受到副作用的影响。需要注意的是,如果不需要特定类型的
列表
,则可以使用
收集器.toList()
。你的答案是从一个
TStream
开始,我想应该是
Stream