hashmap允许一个null键和多个null值,concurrenthashmap和hashtable均禁止null键和null值,差异源于线程安全需求与语义清晰性要求。

Java 中 Map 接口本身不规定 null 键或 null 值是否允许,具体行为由各实现类决定。实际使用中,HashMap 允许一个 null 键和任意多个 null 值;ConcurrentHashMap 则严格禁止 null 键和 null 值。这个差异不是随意设计,而是由线程安全需求和语义清晰性共同决定的。
HashMap:对 null 宽松但有约束
HashMap 是单线程场景下的主力 Map,它在 null 处理上做了明确适配:
- key 为 null 时,哈希值固定为 0,直接存入数组索引 0 的桶中;因此最多只能有一个 null 键,重复 put 会覆盖旧值
- value 为 null 不受限制,不同 key 可对应各自的 null 值,例如
map.put("a", null)和map.put("b", null)同时存在 - get 返回 null 时,无法单凭结果判断是“没找到 key”还是“找到了但 value 是 null”,需配合
containsKey()或getOrDefault(key, default)来区分
ConcurrentHashMap:拒绝 null 以保障并发语义
ConcurrentHashMap 为多线程环境设计,null 键或值会引发歧义,因此源码中在 put 操作起始就检查并抛出 NullPointerException:
- 如果传入 null key,比如
map.put(null, "x"),立刻失败 - 如果传入 null value,比如
map.put("k", null),同样立即报错 - 这种强制校验避免了“返回 null 到底是未命中还是存的就是 null”的模糊问题——在并发读写下,
containsKey()和get()之间可能被其他线程修改,无法保证原子性判断
为什么 Hashtable 也不支持 null?
Hashtable 是早期线程安全 Map,它的限制逻辑更直接:
- put 方法里先调用
key.hashCode(),key 为 null 会直接触发 NPE - 紧接着显式检查
if (value == null) throw new NullPointerException() - 设计初衷是与早期 Java 的严谨性保持一致,且当时尚未引入更精细的并发控制机制,干脆禁用 null 避免歧义
实际编码建议
日常开发中应根据场景主动规避风险:
- 单线程、需要灵活处理空值时,用 HashMap,但记得对 get 结果做必要判断(如用
getOrDefault(key, sentinel)) - 多线程环境一律选用 ConcurrentHashMap,并确保 key 和 value 非 null;可在 put 前加校验,或用 Optional 包装值
- 不要依赖
map.get(key) == null判断 key 是否存在——无论哪种 Map,这都不可靠
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











