-
数据结构:数组+链表+红黑树(jdk1.8引入)
-
默认使用数组(Node<K,V>[] table)存储元素,每个数组元素称为桶
-
哈希冲突时,会将冲突的key-value放入一个桶内,同一桶内元素以链表形式存在(拉链法)
-
JDK 1.8 优化:当链表长度超过阈值(默认 8)且数组长度 ≥ 64 时,链表转换为红黑树,以降低查询时间复杂度(从 O(n) 优化为 O(log n))
-
-
哈希函数
-
通过
hash(key)
计算键的哈希值,决定键值对在数组中的位置。 -
JDK 1.8 优化:哈希计算简化为一次位运算(高位参与异或),减少哈希碰撞概率
-
-
冲突解决
-
拉链法:冲突的键值对以链表或红黑树形式存储在同一个桶中
-
-
扩容机制
-
触发条件:当元素数量超过阈值(容量 × 负载因子,默认负载因子 0.75)。
-
扩容过程:数组大小翻倍(2^n),并重新计算所有元素的位置(rehash)。
-
JDK 1.8 优化:通过高位判断元素新位置,避免重新计算哈希值:
-
新位置 = 原位置 或 原位置 + 旧容量。
-
-
JDK 1.8 的主要优化
-
红黑树引入
-
目的:解决链表过长导致的查询效率低下问题。
-
阈值:链表长度 ≥ 8 且数组长度 ≥ 64 时转红黑树;红黑树节点数 ≤ 6 时退化为链表。
-
-
扩容效率提升
-
高位掩码法:通过
(e.hash & oldCap) == 0
判断元素是否需要移动到新位置(原索引 + oldCap),避免重新计算哈希值。
-
-
尾插法替代头插法
-
JDK 1.7 问题:多线程扩容时头插法可能导致环形链表,引发死循环。
-
JDK 1.8 解决:改用尾插法,避免环形链表(但 HashMap 仍非线程安全)。
-
-
哈希函数简化
-
通过一次位运算(高位异或)替代多次扰动计算,平衡性能与哈希分布
-
拉链法(Separate Chaining)详解
拉链法是解决哈希表(Hash Table)哈希冲突的一种经典方法。其核心思想是:当多个键(Key)经过哈希函数计算后得到相同的哈希值(即落在同一个“桶”中)时,将这些键值对以链表形式存储在同一个桶内