请考虑以下代码段:
List<String> list = new LinkedList<>();
list.add("Hello");
list.add("My");
list.add("Son");
for (String s: list){
if (s.equals("My")) list.remove(s);
System.out.printf("s=%s, list=%s\n",s,list.toString());
}
Run Code Online (Sandbox Code Playgroud)
这导致输出:
s = Hello,list = [Hello,My,Son]
s = My,list = [Hello,Son]
很明显,循环只输入两次,第三个元素"Son"永远不会被访问.从底层的库代码中,看起来发生的是hasNext()迭代器中的方法不检查并发修改,只检查下一个索引的大小.由于remove()调用后大小减少了1 ,因此循环不会再次输入,但不会抛出ConcurrentModificationException.
这似乎与迭代器的契约相矛盾:
list-iterator是快速失败的:如果在创建Iterator之后的任何时候对列表进行结构修改,除了通过list-iterator自己的
remove或add方法之外,list-iterator将抛出一个ConcurrentModificationException.因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险.
这是一个错误吗?同样,迭代器的契约在这里看起来肯定是不服从的 - 列表的结构在迭代过程中由迭代器之外的其他东西进行结构修改.
阅读类级 Javadoc:
请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在不同步并发修改的情况下不可能做出任何硬保证。快速失败迭代器会尽力抛出 ConcurrentModificationException。因此,编写依赖于此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。
| 归档时间: |
|
| 查看次数: |
326 次 |
| 最近记录: |