1.HashMap 的类继承关系
图示即为 Map
相关类的继承关系。源码中的类签名如下:
public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {......
}
2.HashMap 的底层存储结构
HashMap
的底层存储结构是 Node
类, 实现了 Map
中的 Entry
。即 HashMap
底层是由 Node
数组实现存储的。
3.JDK 1.8 中 HashMap 中重要的几个知识点
3.1 数组容量始终为 2 的 N 次幂
HashMap
的数组容量始终为 2 的 N 次幂。原因是当数组容量为 2 的 N 次幂时, hash()
方法计算出来的 hash
值直接与(数组容量 - 1
) 进行 &
(位与)运算就可以得到元素在数组中的下标。
3.2 链表与红黑树的相互转化条件
- 链表转红黑树: 链表长度大于等于
8
, 且数组的长度大于64
。注意, 链表长度大于等于8
一个条件并不会把链表转化为红黑树, 只有两个条件都满足才会触发链表转红黑树的操作, 否则会触发一次resize()
。 - 红黑树转链表: 红黑树的节点小于等于
6
时。
4.HashMap 在 JDK 1.7 中的死锁问题
HashMap
在 JDK 1.7
版本中, 在并发写入的情况下可能会出现死锁的问题。这主要是因为链表的插入方式采用的是头插法。
5.JDK 1.8 进行的优化
- 数组+链表改成了数组+链表or红黑树
- 链表的插入方式从头插法变成了尾插法
- 扩容与插入顺序的变化:
JDK 1.7
中, 先判断是否需要扩容再插入,JDK 1.8
中, 先插入再判断是否需要扩容 - 扩容时重新定位元素位置的方法不同
-JDK 1.7
中, 需要调用hashcode
方法来重新定位在新数组的位置
-JDK 1.8
中, 直接使用了JDK 1.7
中的规律, 即扩容后新数组中的位置 = 原位置or原位置+旧容量