putIfAbsent()仅在键完全不存在时插入新值,若键已存在(含值为null)则不操作;其“完全不存在”指map.containsKey(key)为false,且支持null值插入,是线程安全的原子操作。

Map.putIfAbsent() 的核心作用就是:只在键 完全不存在 时才插入新值,若键已存在(哪怕对应值为 null),就什么也不做,更不会覆盖原有值。这是它和 put() 最关键的区别。
理解“完全不存在”的准确含义
所谓“完全不存在”,指的是 map.containsKey(key) == false —— 即该 key 在 map 中连映射条目都没有。注意以下两种常见误解:
- 如果 key 存在但 value 是
null(例如通过map.put(key, null)显式存入),putIfAbsent(key, v)仍会返回null,且 不会插入新值,因为 key 已存在; - 如果 key 不存在,即使传入的 value 是
null,putIfAbsent(key, null)也会执行插入(即把null作为新值存入)。
典型安全写法示例
适用于初始化默认对象、避免重复创建、线程安全初始化等场景:
Map<String, List<String>> userTags = new ConcurrentHashMap<>();
// 安全地为用户首次添加标签列表:只有 user1 第一次调用时才新建 ArrayList
userTags.putIfAbsent("user1", new ArrayList<>());
// 后续可放心 add,无需再判空
userTags.get("user1").add("vip");
userTags.get("user1").add("active");
这里的关键是:new ArrayList() 只在 key 不存在时才执行,避免了无谓的对象创建,也杜绝了覆盖已有 list 的风险。
与 put() 和 computeIfAbsent() 的对比
-
put(key, value):无条件覆盖,不管 key 是否存在、原值是什么; -
putIfAbsent(key, value):仅当 key 不存在时插入,value 可为 null,不支持延迟计算; -
computeIfAbsent(key, mappingFunction):更强大,key 不存在时才调用函数生成值(支持懒加载、避免创建无用对象),推荐用于需要复杂初始化逻辑的场景。
注意事项
使用时需确保 key 的 equals() 和 hashCode() 实现正确,否则“是否存在”判断会失效;在并发 map(如 ConcurrentHashMap)中,putIfAbsent() 是原子操作,天然线程安全;但在非并发 map(如 HashMap)中,多线程下仍需外部同步。

















