java streams减少操作的意外副作用
在流上调用“reduce”时,我观察到了底层集合的副作用。这是很基本的。我不敢相信我所看到的,但我找不到错误。下面是代码和结果输出。有人能向我解释为什么流的一个底层集合正在变异吗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
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
…