根据this question,我们可以修改源,这不称为干扰:
您可以修改流元素本身,不应将其称为“干扰”。
根据this question,代码
List<String> list = new ArrayList<>();
list.add("test");
list.forEach(x -> list.add(x));
将抛出ConcurrentModificationException
。
但是我的代码,
Employee[] arrayOfEmps = {
new Employee(1, "Jeff Bezos"),
new Employee(2, "Bill Gates"),
new Employee(3, "hendry cavilg"),
new Employee(4, "mark cuban"),
new Employee(5, "zoe"),
new Employee(6, "billl clinton"),
new Employee(7, "ariana") ,
new Employee(8, "cathre"),
new Employee(9, "hostile"),
new Employee(10, "verner"),
};
Employee el=new Employee(1, "Jeff Bezos");
List<Employee> li=Arrays.asList(arrayOfEmps);
li.stream().map(s->{s.setName("newname");return s;}).forEach(System.out::print);
即使实际上更改了源,也不会抛出ConcurrentModificationException
。
这段代码
Employee[] arrayOfEmps = {
new Employee(1, "Jeff Bezos"),
new Employee(2, "Bill Gates"),
new Employee(3, "hendry cavilg"),
new Employee(4, "mark cuban"),
new Employee(5, "zoe"),
new Employee(6, "billl clinton"),
new Employee(7, "ariana") ,
new Employee(8, "cathre"),
new Employee(9, "hostile"),
new Employee(10, "verner"),
};
Employee el=new Employee(1, "Jeff Bezos");
List<Employee> li=Arrays.asList(arrayOfEmps);
li.stream().map(s->{s.setName("newname");li.add(s);return s;}).limit(10).forEach(System.out::print);
抛出
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(Unknown Source)
at java.util.AbstractList.add(Unknown Source)
at java8.Streams.lambda$0(Streams.java:33)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
因此,我不完全了解允许对源代码进行哪些类型的修改,而哪些则不允许。看到一个示例,该示例会产生干扰,并产生有状态和副作用的流,并正确指示出哪个是非常有帮助的。
答案 0 :(得分:12)
执行此操作时:
li.stream().map(s->{s.setName("newname");return s;})
您并没有更改列表本身,而是更改了列表中的一个元素;因此它不会触发您期望的ConcurrentModificationException
。
在最后一个代码段中,您正在使用Array.asList
。
您必须知道Array.asList
在数组(特定的内部ArrayList 类)上仅返回只读包装器,解释了为什么add
不是支持的。
实际上,此内部类不会按设计覆盖 AbstractList#add 方法;导致UnsupportedOperationException
;仍然不像您期望的那样ConcurrentModificationException
。
以下是与您的上一片段类似的示例,该示例确实抛出了ConcurrentModificationException
:
public static void main(String[] args) {
Employee[] arrayOfEmps = {
new Employee(1, "Jeff Bezos")
};
Employee el = new Employee(11, "Bill Gates");
List<Employee> li = new ArrayList<>(Arrays.asList(arrayOfEmps)); // to avoid read-only restriction
li.stream().peek(s -> li.add(el)).forEach(System.out::print);
}
请注意,我将Arrays.List
的返回值包装为“ true” ArrayList
,允许编写,因为该实现实现了add
方法;)
答案 1 :(得分:5)
您的第一个示例更改了Stream
的现有元素,但是没有从源中添加或删除元素。因此,这不是干扰。
您的第二个示例尝试通过在Stream
管道中向源添加元素来进行干扰。但是,由于尝试将元素添加到固定大小的UnsupportedOperationException
(由ConcurrentModificationException
返回)中,因此您得到的是List
而不是Arrays.asList
。
将第二个示例更改为:
List<Employee> li=new ArrayList<>(Arrays.asList(arrayOfEmps));
li.stream().map(s->{s.setName("newname");li.add(s);return s;}).limit(10).forEach(System.out::print);
您应该得到ConcurrentModificationException
。
答案 2 :(得分:4)
在Stream
的来源发生非结构性更改时,这称为结构性。例如ArrayList
文档说:
仅设置元素的值不是结构上的修改...
因此,在您的示例中,这意味着每秒更改Employee
本身不会更改List
本身(不会删除或添加元素)。但是更改List
本身将失败,并显示ConcurrentModificationException
:
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.stream().forEach(x -> list.remove(x));
但是有些来源的干扰大于OK,称为弱一致性遍历,例如ConcurrentHashMap
:
ConcurrentHashMap<Integer, String> chm = new ConcurrentHashMap<>();
chm.put(1, "one");
chm.put(2, "two");
chm.put(3, "three");
chm.entrySet().stream()
.forEach(x -> chm.remove(x.getKey()));
这不会失败,ConcurrentModificationException
。
答案 3 :(得分:2)
您无法更改正在处理的列表的大小。
在第一个示例中,您正在列表中更改Employee对象的值。
第二秒钟,您将在列表中添加一个项目。
那是不同的!
答案 4 :(得分:2)
您的代码正在修改流元素,而不是列表本身:
s->{s.setName("newname")
更改流元素中的字段名称,这不会干扰流或其源。
java.lang.UnsupportedOperationException
也不相关,这是由于您尝试在固定大小的列表上调用add
(这是Arrays.asList
的结果)引起的。
每当有人根据add
的结果调用remove
,Arrays.asList
等时,都会产生不支持的操作异常,因为收集的大小是固定的。
答案 5 :(得分:2)
您的第一个示例不会不修改初始列表。
流只是列表上的视图,因此初始列表完全不受的影响。 >
第二个示例显式使用了list.add()
。
这就是全部。