HashMap



HashMap采用Hash表存储数据,由数组+链表的结构生成。数组的位置由key的hashcode的方法生成,每个数组位置存储一个Entry对象。因为数组的位置是由hashcode通过特定的算法生成的,所以会发生hash冲突。
当发生hash冲突时,将会把新的元素对象通过链表的方式连接到目标数组元素的首部/尾部(jdk1.7采用头插法,jdk1.8采用尾插法),当链表的长度大于8时,链表将会转换为红黑树。
源码解析
主要的属性
使用hash表存储,容器是一个数组:

实际添加的元素数量由size属性记录:

数组的默认容量:

数组的最大容量:

当向数组中不断的添加元素时,超过threshold扩容边界,就会发生扩容:

而扩容边界是由扩容因子决定,threshold = 当前数组的容量 * loadFactor :

默认的扩容因子值为 0.75,如果是一个16容量的数组,当用了12个容量位置时(16 * 0.75 = 12),就会触发扩容操作。

实际扩容时,将会按照二倍扩容的方式对数组进行扩容。
构造器

分别指定初始容量以及扩容因子,如果不传则使用默认值16以及0.75。
其中边界值threshold的大小生成是通过如下方法决定的,他始终会返回一个2^n的数:

put方法

其中,使用key计算目标的hash值(使用key的hashCode右移16位):

然后调用putVal方法,其中,putVal方法的具体逻辑如下:
resize 数组的扩容
加载因子为什么是 0.75
如果扩容因子为1: 只有到16位置都满了之后才会发生扩容。虽然这时候空间利用率极大,但是会很容易发生hash碰撞,特别是剩余的空间不多时,这时候查询效率就会越低。
如果扩容因子为0.5:这时碰撞的概率极低,所以产生链表的概率也低,查询效率极高,但是空间利用率极低。
0.75是对时间与空间的成本上的折中。
主数组的长度为什么必须为2的n次方?
确定目标key在数组的位置上是使用
(length - 1) & hash的方式产生的,等效于length % hash,而如果length的长度不是2的次幂,则这个等效就不会成立为了防止hash冲突

image-20220303225229365
最后更新于
这有帮助吗?