HashMap中key为null时一定落在数组索引0位置,因为源码中对null key的哈希值强制返回0,且下标计算(n-1)&0恒为0,put和get均直接操作table[0]。

HashMap中key为null时为什么一定落在数组索引0位置
因为JDK源码里对 null key做了特殊处理:在计算哈希值时直接返回0,而数组下标由 (n - 1) & hash 得出,当 hash == 0 且初始容量是2的幂(如默认16),(16 - 1) & 0 结果恒为0。
这不是巧合或约定,是明确写死的逻辑——HashMap.get() 和 HashMap.put() 都会先检查 key == null,然后直接去 table[0] 查找或插入。
-
put()中:遇到nullkey,跳过常规哈希计算,走putForNullKey()分支(JDK7)或类似逻辑(JDK8+) -
get()中:同样优先调用getForNullKey(),只遍历table[0]处的链表或红黑树节点 - 即使扩容后数组长度变化(如从16→32),
(32 - 1) & 0还是0,所以nullkey 始终锚定在索引0
debug时怎么确认null key确实存在table[0]里
别靠猜,用IDE调试器直接看 table 字段值,或者用反射临时读取(仅限排查):
Map<string integer> map = new HashMap(); map.put(null, 42); // 此时 map.table[0] 应该是非null的Node/TreeNode数组元素 </string>
注意:JDK8+ 中如果 table[0] 是红黑树节点,table[0] 的类型可能是 TreeNode;如果是链表,则是 Node。但无论哪种,只要 map.containsKey(null) 返回 true,就说明它一定在 table[0] 上。
- 不要用
map.entrySet().stream().filter(...)来“找”null key——它可能被迭代器跳过或顺序不固定 -
map.size()包含nullkey 的计数,但不反映存储位置 - 如果
table[0] == null却能get(null)成功,说明你正在看的是未触发初始化的懒加载状态(table尚未分配)
多个null key插入会发生什么
只会保留最后一个。因为 HashMap 不允许重复 key,而所有 null key 被视为“相等”——put() 时会触发 == null 判断 + value 替换,不会新增节点。
- 第一次
put(null, "a")→table[0]新增一个节点 - 第二次
put(null, "b")→ 在table[0]的链表/树中找到已有nullkey 节点,仅更新value - 没有冲突、不触发扩容、不改变结构,只是简单覆盖
- 这也意味着:无法通过
HashMap存储多个语义不同的 “空键” —— 它们在逻辑上就是同一个 key
自定义类作key时,重写equals但忘了hashCode会怎样
跟 null key 无关,但常被混淆:如果你的类实例可能为 null,又误以为“只要重写了 equals 就能安全当 key”,那就错了。真正影响定位的是 hashCode() 返回值,而不是 equals 本身。
例如:new Person(null) 作为 key 插入后,若其 hashCode() 每次返回不同值(比如基于未初始化字段计算),那么后续 get() 几乎必然失败——因为哈希值变了,查找会去错的桶。
-
nullkey 是特例,绕过了hashCode()调用;普通对象 key 必须保证hashCode()稳定 - 哪怕
equals()写得再完美,只要hashCode()不满足“相等对象必须有相同哈希值”,HashMap就不可靠 - IDE生成的
hashCode()通常没问题,但手写时容易漏掉字段、用随机数、或依赖可变状态
真正难的不是记住 null 在索引0,而是理解哈希表底层靠 hash 定位、靠 equals 确认——这两个环节缺一不可,而 null 只是前者被硬编码绕过的个例。










