You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HashMap添加元素时抛出ConcurrentModificationException的原因与解决

HashMap遍历中添加元素触发ConcurrentModificationException的原因及解决办法

问题描述

我编写了一段统计字符串中字母出现次数的代码,用HashMap存储字母与对应次数,代码如下:

Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)),
            numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
   for (String key : letters.keySet()) {
      if (!letters.containsKey(String.valueOf(input.charAt(i)))) {
         letters.put(String.valueOf(input.charAt(i)),
                     numberOfLettersInWord(input,input.charAt(i)));
      } else continue;
      System.out.println(letters);
   }
   System.out.println(1);
}
System.out.println(2);

其中numberOfLettersInWord方法可正确统计单个字母的出现次数,但运行时抛出java.util.ConcurrentModificationException异常,异常栈信息如下:

1
1
{a=4, b=4}
1
1
1
1
{a=4, b=4, c=3}
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1579)
    at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1602)
    at LetterCounter.count(LetterCounter.java:25)
    at LetterCounter.main(LetterCounter.java:11)

Process finished with exit code 1

测试输入为String input = "aabbabcccba",代码能正确添加字母及次数,但在无新字母可添加时抛出异常。我了解该异常常出现在删除元素场景,但不确定添加元素时是否适用相同解决方案,希望了解异常原因及解决办法。

异常原因

ConcurrentModificationException是HashMap的**快速失败(fail-fast)**机制触发的。HashMap的迭代器(包括遍历keySet()时生成的迭代器)会维护一个expectedModCount值,与HashMap内部的modCount(记录结构修改次数,比如添加、删除元素)保持一致。当你在遍历keySet()的过程中调用put()添加新元素时,HashMap的modCount会递增,但迭代器的expectedModCount不会同步更新,下一次迭代器调用next()时发现两者不一致,就会抛出该异常。

注意:快速失败机制仅用于调试阶段发现非预期的并发修改,不保证100%能检测到,不能依赖它处理多线程场景。

解决办法

1. 优化统计逻辑(推荐)

原代码嵌套遍历keySet()属于冗余逻辑,直接遍历字符串即可完成统计,既避免异常又提升效率:

Map<String, Integer> letters = new HashMap<>();
for (int i = 0; i < input.length(); i++) {
    String charStr = String.valueOf(input.charAt(i));
    if (!letters.containsKey(charStr)) {
        letters.put(charStr, numberOfLettersInWord(input, input.charAt(i)));
    }
}
System.out.println(letters);

这种方式将时间复杂度从O(n*m)(n为字符串长度,m为Map键数量)降低到O(n),彻底规避了遍历中修改Map结构的问题。

2. 使用ConcurrentHashMap替代HashMap

ConcurrentHashMap是线程安全的Map实现,它的迭代器是**弱一致(weakly consistent)**的,遍历过程中修改结构不会抛出ConcurrentModificationException,但不保证迭代器能立即看到最新修改:

Map<String, Integer> letters = new ConcurrentHashMap<>();
letters.put(String.valueOf(input.charAt(0)),
            numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
   for (String key : letters.keySet()) {
      String charStr = String.valueOf(input.charAt(i));
      if (!letters.containsKey(charStr)) {
         letters.put(charStr, numberOfLettersInWord(input, input.charAt(i)));
      }
      System.out.println(letters);
   }
   System.out.println(1);
}
System.out.println(2);

此方法保留了原代码的嵌套逻辑,但效率较低,仅建议在无法修改核心遍历逻辑时使用。

内容的提问来源于stack exchange,提问作者Ra'Uko Lord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 08:10:22