put方法返回被替换的旧值,key不存在时返回null;它不是布尔结果,而是旧值快照,用于原子性操作,所有标准map实现均遵循此规则。

put 方法返回值到底是什么
当 put 向 Map 插入一个已存在的 key 时,它会用新值覆盖旧值,并**返回被替换掉的旧值**;如果 key 之前不存在,则返回 null。这个返回值不是操作是否成功的布尔标记,而是旧值的“快照”——它只告诉你“原来那里有什么”,不反映当前状态。
为什么返回旧值而不是 boolean
Java 设计者刻意让 put 返回旧值,是为了支持原子性判断+替换场景。比如你不能靠 map.containsKey(k) && map.put(k, v) 来安全更新,因为中间可能有并发写入;而直接用 Object old = map.put(k, v) 一步拿到旧值,既避免重复查表,又天然线程安全(对单次调用而言)。
-
HashMap、ConcurrentHashMap、TreeMap全部遵循这一契约 - 若你传入的
value是null,且原key对应的旧值也是null,返回值仍是null—— 此时无法区分是首次插入还是覆盖了null - 某些实现(如
ConcurrentHashMap的put)在高并发下仍保证返回值语义不变,但不保证其他线程立即看到新值
常见误用和坑点
最容易出错的是把返回值当成操作结果标识:
- 写成
if (map.put(k, v) != null) { ... }想判断“是否覆盖”,但若旧值本身就是null,条件就失效了 - 在
for循环里反复put并依赖返回值做分支逻辑,却忘了key可能第一次就不存在,导致null被当作“未覆盖”误判 -
LinkedHashMap的put返回值行为一致,但如果你启用了访问顺序(accessOrder=true),覆盖操作还会触发节点移动 —— 返回值不变,但内部结构变了
替代方案:更明确的语义选择
如果你真正需要的是“仅当 key 不存在时才插入”,别硬套 put,改用 putIfAbsent:
map.putIfAbsent(key, value); // 返回旧值(存在时)或 null(插入成功时)
或者想严格区分三种状态(新增 / 覆盖 / 无变化),得自己查再决定:
Object oldValue = map.get(key);
if (oldValue == null && !map.containsKey(key)) {
map.put(key, newValue); // 确实是新增
} else if (oldValue != null) {
map.put(key, newValue); // 覆盖非 null 值
}
注意:containsKey 和 get 组合在并发 Map 中不安全,这种逻辑只适用于单线程或外部加锁场景。










