为什么这段代码不会抛出ConcurrentModificationException?
为什么这段代码不会抛出ConcurrentModificationException
? 它在迭代过程中修改了Collection
,而没有使用Iterator.remove()
方法,这意味着它是唯一安全的删除方法 。
List strings = new ArrayList(Arrays.asList("A", "B", "C")); for (String string : strings) if ("B".equals(string)) strings.remove("B"); System.out.println(strings);
如果我用LinkedList
替换ArrayList
,我得到相同的结果。 但是,如果我将列表更改为("A", "B", "C", "D)
或只是("A", "B")
我会按预期得到exception。发生了什么?我正在使用jdk1.8.0_25
如果相关的话。
编辑
我找到了以下链接
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078
相关部分是
天真的解决方案是在AbstractList中为hasNext添加编码检查,但这会使编纂检查的成本翻倍。 事实certificate,仅在最后一次迭代时进行测试就足够了,这几乎不会增加成本。 换句话说,hasNext的当前实现:
public boolean hasNext() { return nextIndex() < size; }
被此实现取代:
public boolean hasNext() { if (cursor != size()) return true; checkForComodification(); return false; }
由于Sun内部监管机构拒绝了此项更改,因此不会进行此更改。 正式裁决表明,这一变化“已certificate可能对现有代码产生重大的兼容性影响。” (“兼容性影响”是修复程序有可能用ConcurrentModificationException替换静默不当行为。)
作为一般规则,在检测到修改时抛出ConcurrentModificationException
,而不是引起修改。 如果您在修改后从未访问过迭代器,则不会抛出exception。 遗憾的是,这一细节使得ConcurrentModificationException
对于检测数据结构的滥用非常不可靠,因为它们仅在损坏完成后被抛出。
此方案不会抛出ConcurrentModificationException
因为修改后未在创建的迭代器上调用next()
。
for-each循环实际上是迭代器,所以你的代码实际上是这样的:
List strings = new ArrayList<>(Arrays.asList("A", "B", "C")); Iterator iter = strings.iterator(); while(iter.hasNext()){ String string = iter.next(); if ("B".equals(string)) strings.remove("B"); } System.out.println(strings);
考虑在您提供的列表上运行的代码。 迭代看起来像:
-
hasNext()
返回true,输入循环, – > iter移动到索引0,字符串=“A”,未删除 -
hasNext()
返回true,继续循环 – > iter移动到索引1,字符串=“B”,删除。strings
现在长度为2。 -
hasNext()
返回false(iter当前位于最后一个索引,不再有索引),退出循环。
因此,当对next()
的调用检测到已经进行了修改时抛出了ConcurrentModificationException
,这种情况可以避免这种exception。
对于您的其他两个结果,我们会得到例外。 对于"A", "B", "C", "D"
,在删除“B”之后我们仍然在循环中,而next()
检测到ConcurrentModificationException
,而对于"A", "B"
我想象它是某种ArrayIndexOutOfBounds被捕获并作为ConcurrentModificationException
重新抛出
ArrayList的迭代器中的hasNext
就是
public boolean hasNext() { return cursor != size; }
在remove
调用之后,迭代器位于索引2,列表的大小为2,因此它报告迭代完成。 没有并发修改检查。 使用(“A”,“B”,“C”,“D”或(“A”,“B”),迭代器不在列表的新末尾,因此调用next
,并抛出exception。
ConcurrentModificationException
只是一个调试辅助工具。 你不能依赖它们。
@Tavian Barnes完全正确。 如果有问题的并发修改未同步,则无法保证抛出此exception。 引用java.util.ConcurrentModification
规范:
请注意,无法保证快速失败的行为,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证。 失败快速操作会尽最大努力抛出ConcurrentModificationException。 因此,编写依赖于此exception的程序以确保其正确性是错误的:ConcurrentModificationException应仅用于检测错误。
链接到JavaDoc以获取ConcurrentModificationException