【问题标题】:Putting into HashMap at 0 index is causing ConcurrentModificationException在 0 索引处放入 HashMap 会导致 ConcurrentModificationException
【发布时间】:2025-12-07 12:55:01
【问题描述】:

我有一个简单的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);

一切正常

【问题讨论】:

  • ConcurrentModificationException 在您修改当前正在迭代的集合时发生。迭代器不会被通知基础集合的更改。创建要迭代的集合的副本,以避免这种情况。第一个工作正常,因为您不更改集合的大小。
  • map.put(1, 2); 工作正常 - 我会理解是否每次都会抛出异常但为什么它只发生在 &lt;=0 键上?
  • put(100000,x) 也会这样做
  • 在这个特定的 hasmap 迭代器实现中,重要的是集合的大小,而不是实际的键值对

标签: java collections


【解决方案1】:

请参阅Javadoc(已添加重点):

这个类的所有“集合视图方法”返回的迭代器都是快速失败的:如果在迭代器创建后的任何时间对映射进行结构修改,除了通过迭代器自己的删除方法之外,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

结构修改前面定义为:

结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。

所以:

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

【讨论】:

    【解决方案2】:

    您在导致异常的迭代期间更改了地图的大小。请记住,引发异常的不是put 操作,而是通过Iterator#next 尝试通过迭代器获取下一个元素

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

    【讨论】:

    • 那么为什么map.put(1, 2); 工作正常?
    • 因为你没有改变地图的大小。你从 X 元素开始,然后放置它的仍然 X 元素 - 你用新值替换了现有的键。 put (0,x) 添加新元素,从而改变地图的大小。任何其他 put(notYetInTheMap,x) 都会导致此问题以及元素的删除
    • 这是有道理的,但是当我在12 有元素然后执行map.put(0, 2) 时,为什么这会起作用? (请看我的编辑)
    • @m.antkowicz 那么没关系,因为它是迭代器的最后一次迭代,没有更多的hasNext() 检查。
    • 正如我所写,迭代器在 next() 调用期间抛出异常,因此没有调用 == 没有异常。
    【解决方案3】:

    它不仅仅与小于 0 的键相关。

    首先它可以与 ma​​p.put(1, 2); 一起使用,因为在这里您不会将新项目添加到地图,您只需将现有值替换为另一个。

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

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

    【讨论】:

      最近更新 更多