hashmap支持null键(仅一个)和任意null值,null键存于table[0]并特殊处理哈希与查找;concurrenthashmap禁用null以避免并发下语义歧义。

HashMap 明确支持 null 键和 null 值,但有严格限制:只允许一个 null 键,允许多个 null 值。这不是“没检查就侥幸通过”,而是源码中专门写了分支逻辑来处理。
Null 键怎么存、怎么查
HashMap 对 null 键做了特殊路径处理:
- put(null, value) 时,不调用 key.hashCode(),直接把哈希值定为 0,存入数组索引 0 的桶(table[0])
- 后续再 put(null, newValue),不会新增节点,而是覆盖 table[0] 中已有 null 键对应的 value
- get(null) 也不走常规哈希计算,直接去 table[0] 链表或红黑树里遍历,用 == 判断 key 是否为 null(不是 equals)
- containsKey(null) 返回 true/false 准确可靠;但 get(null) == null 无法区分“没存过”还是“存过但值是 null”
Null 值可以随便放吗
null 值没有位置或数量限制,只要 key 不同,就能各自对应一个 null:
- map.put("a", null)、map.put("b", null)、map.put(123, null) 全部合法
- value 为 null 不影响哈希计算、桶定位或结构操作
- 但 get("a") 返回 null 时,你无法单凭这个结果判断是“键不存在”还是“值就是 null”
- 需要区分时,优先用 containsKey("a"),或改用 getOrDefault("a", DEFAULT) 提供默认值
为什么 ConcurrentHashMap 不让用 null
不是技术做不到,而是并发场景下语义会混乱:
- ConcurrentHashMap.put(null, "x") 和 put("k", null) 都会在入口直接抛 NullPointerException
- 因为 get(key) 返回 null 时,在多线程中无法原子确认是“未命中”还是“存的就是 null”
- containsKey() 和 get() 之间可能被其他线程修改,导致判断失效
- 去掉 null 支持,能让返回值含义唯一:null = 键不存在
实际写代码要注意什么
别让 null 键/值变成隐患:
- 避免用 get(key) == null 判断键是否存在——一律改用 containsKey(key)
- 如果业务真需要表达“无值”状态,又怕和“键不存在”混淆,建议用 Optional
包装 value,而不是塞 null - 从 HashMap 切换到 ConcurrentHashMap 时,务必检查所有 put 操作,提前过滤 null 键和 null 值
- 自定义 key 类时,即使重写了 hashCode() 抛异常,HashMap 的 null 键依然安全——它根本不会调那个方法
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











