HashMap底层是“数组+链表/红黑树”组合结构,主干为Node[]数组(默认长16、容量2的幂),每个桶存链表头或红黑树根;哈希计算先扰动再(n-1)&hash定位;冲突时链表存储,≥8且数组≥64转红黑树,≤6退化回链表;null键固定存table[0]。

HashMap 的底层哈希表,本质是一个“数组 + 冲突处理结构”的组合体,不是单纯一张表,而是一套动态协作的存储机制。
数组是主干,每个位置叫一个“桶(Bucket)”
HashMap 内部维护一个 Node[] 类型的数组(叫 table),默认长度是 16,且容量始终是 2 的幂(如 16、32、64…)。这个数组就是哈希表的骨架。数组每个下标位置就是一个“桶”,它不直接存 key-value,而是存链表头节点或红黑树根节点的引用。
- 桶为空:表示该位置没数据,插入时可直接放新节点
- 桶非空:说明已有元素映射到这个位置,发生了哈希冲突,需进一步处理
哈希值决定进哪个桶,但不是简单取模
把 key 映射到桶的过程分两步:先算 hash 值,再定位索引。
- 先调用 key.hashCode(),再做扰动运算:(h = key.hashCode()) ^ (h >>> 16),让高16位参与运算,降低碰撞概率
- 再用 (n - 1) & hash 算出下标(n 是数组长度),这等价于 hash % n,但位运算更快,也要求 n 必须是 2 的幂
同一个桶里怎么存多个键值对?靠链表和红黑树
不同 key 算出相同下标,就产生哈希冲突。HashMap 用链地址法解决——所有冲突元素都挂在同一个桶后面。
- 元素少时:用 Node 链表,JDK 1.8 起是尾插法(避免多线程死循环)
- 链表变长后:当桶内节点数 ≥ 8 且数组长度 ≥ 64,链表自动转为 TreeNode 红黑树,查找从 O(n) 降到 O(log n)
- 节点减少后:红黑树中节点 ≤ 6,又退化回链表,省去树维护开销
null key 是个特例,固定存在第 0 号桶
HashMap 允许一个 null 键。它的哈希值被硬编码为 0,所以 null key 总是落在 table[0] 这个桶里。后续操作(put/get)都会单独判断 key == null 的情况,走专门逻辑,不走常规 hash 计算流程。


















