putIfAbsent 是 HashMap 在 Java 8 中引入的原子性插入方法,仅当键不存在时插入值并返回 null,否则返回已有值;它线程不安全,适用于单线程或已加锁场景,不可替代 ConcurrentHashMap 的同名方法。

putIfAbsent 是 HashMap 在 Java 8 中引入的线程不安全但原子性保障的插入方法,它只在键不存在时才插入新值,避免覆盖已有数据,适合单线程或已加锁场景下的“有则不动、无则设值”逻辑。
核心行为:只在 key 不存在时插入
该方法签名是:V putIfAbsent(K key, V value)。它会检查 key 是否已存在:
- 如果 key 不存在,插入
(key, value),返回null - 如果 key 已存在,不修改 map,直接返回当前关联的旧值(非 null)
注意:它不是线程安全的——多个线程并发调用 putIfAbsent 可能导致覆盖或重复插入,不能替代 ConcurrentHashMap 的同名方法。
典型使用场景与写法
常见于缓存初始化、默认配置设置、首次注册等逻辑:
- 给用户设置默认头像:
avatars.putIfAbsent(userId, "default.png"); - 初始化某个任务的状态容器:
taskStatuses.putIfAbsent(taskId, new AtomicBoolean(false)); - 避免重复创建对象:
cache.putIfAbsent(key, computeExpensiveValue());(computeExpensiveValue 只在 key 不存在时执行)
和 get + put 对比:更简洁且避免竞态
手动实现类似逻辑容易出错:
// ❌ 错误示范:非原子,可能被其他线程干扰
if (!map.containsKey(key)) {
map.put(key, value); // 中间可能有其他线程插入了同一 key
}
而 putIfAbsent 是 HashMap 内部一次哈希查找+判断+插入(或跳过),保证这三步对当前 key 是原子的(不考虑整个 map 结构变更)。虽然整个 map 仍非线程安全,但对单个 key 的“不存在即插入”操作不会因并发读写而失效。
注意事项与常见坑
-
null key 支持:HashMap 允许一个 null key,
putIfAbsent(null, "v")有效;但若已有null → "old",再次调用会返回 "old",不会覆盖 -
value 可为 null:传入
putIfAbsent("k", null)是合法的,此时若 key 不存在,会存入"k" → null;若已存在,则返回旧值(可能是 null) -
返回值别忽略:它返回的是“插入前已有的值”,可用于判断是否真正插入成功,例如:
if (map.putIfAbsent(k, v) == null) { /* 确实是首次插入 */ } -
不适用于高并发写场景:如需多线程安全插入,请改用
ConcurrentHashMap的putIfAbsent,它基于 CAS 和锁分段/Node 插入机制实现真正线程安全
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











