为什么在迭代期间从地图中删除不存在的元素有时会崩溃?

时间:2016-04-19 00:12:07

标签: java iterator concurrentmodification

注意:这不是在迭代期间询问如何从地图中删除项目的许多问题的重复。

当使用哈希映射迭代器从地图中删除项目时,我遇到了一些令人惊讶的边缘情况。

以下代码与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有时会崩溃?这是 在标准中指定的任何地方?

1 个答案:

答案 0 :(得分:4)

第一个示例抛出ConcurrentModificationException,因为您在迭代地图期间调用remove方法。事件的顺序是这样的。

  1. 调用next()以检索条目(1,1)。密钥不是2,因此请勿拨打iterator.remove()。通过调用2,直接从地图中删除包含密钥m.remove(2)的条目。这会更改Iterator期望保持不变的内部修改计数。
  2. 调用next()以检索下一个条目。地图中的修改计数不再与Iterator在创建时记录的预期修改计数相匹配,因此会引发ConcurrentModificationException
  3. 最后一个例子不会抛出它,因为remove(2);不再有效。

    1. 调用next()以检索条目(2,2)。密钥为2,因此请调用iterator.remove(),删除条目。这具有修改迭代器的预期修改计数以匹配映射的修改计数的效果。调用m.remove(2)无效,因为地图中不再存在关键字2
    2. 调用next()以检索条目(3,3)。地图中的修改计数与Iterator指出的预期修改计数相匹配,因此不会引发ConcurrentModificationException。密钥不是2,因此不会调用iterator.remove()。调用m.remove(2)无效,因为地图中不存在关键字2
    3. 请注意,上述事件序列对Java HashMap迭代其条目的当前方式有效。密钥恰好按顺序检索。通常,对于任何范围的有效整数键,键保证按顺序检索。