Java HashMap 原理(位桶 + 链表)
不同版本的 JDK 实现 HashMap 的原理有所不同:
JDK 1.6, JDK 1.7 HashMap 采用位桶 + 链表实现。
JDK 1.8 HashMap 采用位桶 + 链表 + 红黑树实现。(当链表长度超过阈值 “8” 时,将链表转换为红黑树)
我们先介绍位桶 + 链表这一种形式。
简单说明
HashMap 的每一个元素,都是链表的一个节点(entry)。
新增一个元素时,会先计算 key 的 hash 值,找到存入数组的位置。
如果该位置已经有节点(链表头),则存入该节点的最后一个位置(链表尾)。
所以 HashMap 就是一个数组(bucket),数组上每一个元素都是一个节点(节点和所有下一个节点组成一个链表)或者为空,显然同一个链表上的节点 hash 值都一样。
HashMap 内部存储
HashMap 实际上是一个链表散列,即数组和链表的结合体。
HashMap 每一个节点都继承于 Map.Entry 。
每个 Map.Entry 包含 Hash、Key、Value、Next 等信息。
HashMap 新增
- 先计算 key 的 hash 值
- hash 值作为 index ,找到对应的数值位置
- 如果数组位置为空,直接存入当前的 Map.Entry
- 如果数组位置为空,则遍历链表,插入末尾。
HashMap 删除
- 先计算 key 的 hash 值
- hash 值作为 index ,找到对应的数值位置
- 遍历该链表,找到目标 Map.Entry 进行删除。
HashMap 容量 (capacity) 和负载因子 (loadFactor)
capacity 是数组(bucket)的大小,loadFactor 是数组的最大填满比例。
当数组中的节点(entry)数目 > c a p a c i t y ∗ l o a d F a c t o r >capacity*loadFactor >capacity∗loadFactor 时,就需要扩容,调整数组的大小为当前的 2 倍,以提高 HashMap 的 hash 效率。
所以数组的初始容量和扩容后的容量必须是