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