在 0 索引处放入 HashMap 会导致 ConcurrentModificationException

Putting into HashMap at 0 index is causing ConcurrentModificationException

我有一个简单的HashMap算法:

HashMap<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);

Iterator<Integer> it = map.keySet().iterator();
while(it.hasNext()) {
    Integer key = it.next();
    if (key.equals(2)) {
        map.put(1, 2);
    }
}

这工作正常。 但是当我将条件体修改为:

if (key.equals(2)) {
    map.put(0, 2); // changed index '1' to '0'
}

它总是抛出 java.util.ConcurrentModificationException。对于小于 0.

的键值,也会发生同样的情况

我错过了什么?


编辑

似乎如果我将删除第三个 Map 元素:

map.put(1, 1);
map.put(2, 2);
// map.put(3, 3);

工作正常

您在迭代期间更改了地图的大小,这导致了异常。请记住,抛出异常的不是 put 操作,而是尝试通过迭代器通过 Iterator#next

获取下一个元素

在您的情况下,如果您“扩展”了地图,迭代器将在 next() 上抛出异常。但是,如果它是在最后一次迭代中完成的,则 hasNext returns 为 false。这将导致跳过 next() 调用并且不会抛出异常。

只对小于0的键不相关

首先它与 map.put(1, 2); 一起工作,因为在这里您没有向地图添加新项目,您只是将现有值替换为另一个。

但是,如果您尝试添加任何具有新索引的新项目(例如您的示例中的 0,但可以有任何新索引,例如 4、5、6,....),您总是会得到 java.util.ConcurrentModificationException.

所以你不能在迭代器中添加新项目。

参考Javadoc(重点加):

The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

结构修改前面定义为:

A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.

所以:

  • map.put(1, 2);不是结构修改,因为键1已经在map中了。您所做的只是更改与之关联的值。没有 ConcurrentModificationException 被抛出。
  • map.put(0, 2); 结构修改,因为键0不在映射中。因此,抛出 ConcurrentModificationException