讲师中心 微信公众号
AI工具推荐 视频效率加速

如何安全地在遍历 HashMap 时动态添加新键值对

千墨大大_3911

千墨大大_3911

发布时间:2026-02-03 23:40:01

|

326人浏览过

|

来源于php中文网

原创

如何安全地在遍历 HashMap 时动态添加新键值对

本文详解 `concurrentmodificationexception` 在遍历 `hashmap` 时调用 `put()` 导致崩溃的根本原因,并提供简洁、线程安全(单线程场景下)且高效的解决方案——使用 `putifabsent()` 或预收集键集,避免迭代器失效。

你遇到的 ConcurrentModificationException 并非源于多线程并发,而是在单线程中违反了“结构修改禁止遍历”这一核心契约。Java 的 HashMap(及其迭代器)采用快速失败(fail-fast)机制:当通过 for-each 循环(本质是调用 keySet().iterator())遍历时,若底层 HashMap 被结构性修改(如 put()、remove()),但迭代器未感知该变更,就会立即抛出此异常。

在你的原始代码中:

for (String key : letters.keySet()) {  // ← 获取 keySet 迭代器
    if (!letters.containsKey(String.valueOf(input.charAt(i)))) {
        letters.put(...); // ← 结构性修改!破坏迭代器预期状态
    }
}

问题在于:外层 for (int i = ...) 每次循环都会启动一次新的 keySet() 遍历;而内层 put() 操作会改变 HashMap 的内部结构(如扩容、链表转红黑树、modCount 增加),导致当前正在使用的迭代器检测到 modCount 不匹配,从而触发异常——即使你并未显式调用 remove(),put() 同样属于结构性修改。

✅ 正确解法:避免在遍历过程中修改集合本身。有以下两种推荐方式:

Gradle 9.8.0
Gradle 9.8.0

Gradle 9.8.0 官方二进制分发包,适用于 Windows、macOS 与 Linux 的 Java、Kotlin 和 Android 项目构建。

下载

方案一:使用 putIfAbsent()(推荐 ✅)

这是最简洁、语义最清晰的修复方式,完全规避遍历与修改的冲突:

Map<String, Integer> letters = new HashMap<>();
// 初始化首字符(可选,实际可省略)
letters.put(String.valueOf(input.charAt(0)), numberOfLettersInWord(input, input.charAt(0)));

// 直接遍历字符串每个字符,按需插入(无条件插入或仅当不存在时插入)
for (int i = 0; i < input.length(); i++) {
    char c = input.charAt(i);
    String key = String.valueOf(c);
    letters.putIfAbsent(key, numberOfLettersInWord(input, c));
}
System.out.println(letters); // 示例输出: {a=4, b=4, c=3}

putIfAbsent(key, value) 是原子操作:仅当 key 不存在时才执行 put,且不依赖当前 keySet() 迭代状态,因此完全绕过 ConcurrentModificationException 风险。

? 提示:你甚至可以删除初始 put(),因为 putIfAbsent 对首个字符也安全有效,代码更统一。

方案二:先收集待插入键,遍历结束后批量插入

若需更复杂的插入逻辑(如依赖已有键值计算新值),可先缓存待添加项:

Set<Character> pendingChars = new LinkedHashSet<>();
for (int i = 0; i < input.length(); i++) {
    pendingChars.add(input.charAt(i)); // 自动去重
}
for (char c : pendingChars) {
    letters.put(String.valueOf(c), numberOfLettersInWord(input, c));
}

⚠️ 注意事项

  • ❌ 不要使用 for-each + put()/remove() 组合,这是 ConcurrentModificationException 的典型诱因;
  • ✅ putIfAbsent()、computeIfAbsent()、merge() 等 JDK 8+ 方法专为安全更新设计,优先选用;
  • ? numberOfLettersInWord() 若存在性能瓶颈(重复统计同一字符),建议改用一次遍历统计算法(如 Map<Character, Integer> 累加),时间复杂度从 O(n²) 降至 O(n);
  • ? 本例为单线程场景;如涉及多线程读写,请改用 ConcurrentHashMap 并注意其 putIfAbsent() 等方法的线程安全性。

总结:ConcurrentModificationException 是 Java 集合框架的保护机制,提醒你“边看边改”不可取。坚持「只读遍历」或选用「原子更新方法」,即可写出健壮、可维护的集合操作代码。

热门AI工具

更多
AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

6499

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3165

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

4068

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

2805

2025.08.29

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4278

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

1189

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

403

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

559

2026.01.21

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

0

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn