注意:这不是在迭代期间询问如何从地图中删除项目的许多问题的重复。
当使用哈希映射迭代器从地图中删除项目时,我遇到了一些令人惊讶的边缘情况。
以下代码与a ConcurrentModificationException
崩溃。
Map<Integer, Integer> m = new HashMap<>();
m.put(1, 1);
m.put(2, 2);
m.put(3, 3);
for (Iterator<Map.Entry<Integer, Integer>> iterator = m.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<Integer, Integer> e = iterator.next();
if (e.getKey() == 2) {
iterator.remove();
}
m.remove(2); // This causes the crash
}
不出所料,以下代码没有:
Map<Integer, Integer> m = new HashMap<>();
m.put(1, 1);
m.put(2, 2);
m.put(3, 3);
for (Iterator<Map.Entry<Integer, Integer>> iterator = m.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<Integer, Integer> e = iterator.next();
if (e.getKey() == 2) {
iterator.remove();
}
m.remove(4); // No crash here
}
但是,以下代码也不会崩溃:
Map<Integer, Integer> m = new HashMap<>();
m.put(2, 2);
m.put(3, 3);
for (Iterator<Map.Entry<Integer, Integer>> iterator = m.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<Integer, Integer> e = iterator.next();
if (e.getKey() == 2) {
iterator.remove();
}
m.remove(2); // Also no crash?
}
第一个和第三个例子之间的唯一区别是删除 &lt; 1,1&gt;条目。为什么调用Map.remove有时会崩溃?这是 在标准中指定的任何地方?
答案 0 :(得分:4)
第一个示例抛出ConcurrentModificationException
,因为您在迭代地图期间调用remove
方法。事件的顺序是这样的。
next()
以检索条目(1,1)。密钥不是2
,因此请勿拨打iterator.remove()
。通过调用2
,直接从地图中删除包含密钥m.remove(2)
的条目。这会更改Iterator
期望保持不变的内部修改计数。next()
以检索下一个条目。地图中的修改计数不再与Iterator
在创建时记录的预期修改计数相匹配,因此会引发ConcurrentModificationException
。最后一个例子不会抛出它,因为remove(2);
不再有效。
next()
以检索条目(2,2)。密钥为2
,因此请调用iterator.remove()
,删除条目。这具有修改迭代器的预期修改计数以匹配映射的修改计数的效果。调用m.remove(2)
无效,因为地图中不再存在关键字2
。next()
以检索条目(3,3)。地图中的修改计数与Iterator
指出的预期修改计数相匹配,因此不会引发ConcurrentModificationException
。密钥不是2
,因此不会调用iterator.remove()
。调用m.remove(2)
无效,因为地图中不存在关键字2
。请注意,上述事件序列对Java HashMap
迭代其条目的当前方式有效。密钥恰好按顺序检索。通常,对于任何范围的有效整数键,键不保证按顺序检索。