Joy*_*eep 5 java concurrentmodification java-8 java-stream
我正在尝试以下代码Java 8 SE 我直接从eclipse运行它,它有下面提到的异常我也用命令提示符运行它产生相同的结果.
List<String> test = new ArrayList<>();
test.add("A");
test.add("B");
test.add("c");
test = test.subList(0, 2);
Stream<String> s = test.stream();
test.add("d");
s.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
我不确定为什么它会给出以下异常
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1388)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
Run Code Online (Sandbox Code Playgroud)
我运行的Java版本
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
Run Code Online (Sandbox Code Playgroud)
List<String> test = new ArrayList<>(Arrays.asList("java-8", "subList", "bug")).subList(0, 2);
Stream<String> stream = test.stream();
test.add("java-9");
stream.forEach(System.out::println); // any terminal operation
Run Code Online (Sandbox Code Playgroud)
上面用 Java-8 执行的代码会抛出一个 CME。根据javadocArrayList
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间以任何方式修改了列表的结构,除了通过迭代器自己的 remove 或 add 方法,迭代器将抛出一个
ConcurrentModificationException.因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒着任意、非确定性行为的风险。
输出:
Run Code Online (Sandbox Code Playgroud)java-8 subList Exception in thread "main" java.util.ConcurrentModificationException
在类似的指导方针下,在迭代时修改集合被认为是一个编程错误,因此ConcurrentModificationException在“尽力而为”的基础上执行抛出。
但接下来的问题是,在上面的代码中,我们是否真的在迭代时或更确切地说之前修改了集合?
流不应该是懒惰的吗?
在进一步搜索此类预期行为时,发现了类似的报告并作为错误修复 - ArrayList.subList().spliterator() 不是后期绑定,这已通过 Java-9 修复。
与此相关的另一个错误 - ArrayList.subList().iterator().forEachRemaining() off-by-one-error
尽管根据错误报告在 Java-9 中进行了修复,但我正在执行的实际测试是在 LTS 版本上进行的,并且上面共享的代码毫无例外地工作。
输出:
Run Code Online (Sandbox Code Playgroud)java-8 subList java-9
这是因为subList,让我用不同的场景来解释
来自 java docs 文档
对于行为良好的流源,可以在终端操作开始之前修改源,这些修改将反映在涵盖的元素中。
除了转义线操作 iterator() 和 spliterator() 外,执行在调用终止操作时开始,并在终止操作完成时结束。
案例1:成功(因为在终端操作开始前可以修改源)
List<String> test = new ArrayList<>();
test.add("A");
test.add("B");
test.add("c");
//test = test.subList(0, 2);
Stream s = test.stream();
test.add("d");
s.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
输出 :
A
B
c
d
Run Code Online (Sandbox Code Playgroud)
案例2:sublist不同参考失败
List<String> test = new ArrayList<>();
test.add("A");
test.add("B");
test.add("c");
List<String> test1 = test.subList(0, 2);
Stream s = test1.stream();
test1.add("d");
s.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
输出 :
A
BException in thread "main"
java.util.ConcurrentModificationException
at
java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1388)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.demo.Example.Main2.main(Main2.java:30)
Run Code Online (Sandbox Code Playgroud)
情况 3:sublist相同引用失败,两个列表不同
List<String> test = new ArrayList<>();
test.add("A");
test.add("B");
test.add("c");
System.out.println(test.hashCode()); //94401
test = test.subList(0, 2);
System.out.println(test.hashCode()); //3042
Stream s = test.stream();
test.add("d");
s.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
输出 :
94401
3042
A
B
Exception in thread "main" java.util.ConcurrentModificationException
at
java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1388)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.demo.Example.Main2.main(Main2.java:32)
Run Code Online (Sandbox Code Playgroud)
定论
如果支持列表(即,此列表)以除返回列表以外的任何方式在结构上进行了修改,则此方法返回的列表的语义将变为未定义。(结构修改是那些改变这个列表的大小,或者以其他方式扰乱它,以致正在进行的迭代可能会产生不正确的结果。)
从文档子列表 文档
| 归档时间: |
|
| 查看次数: |
938 次 |
| 最近记录: |