数据结构(Java):HashMap源码解析【JDK17】
HashMap源码解读
·
目录
1、整体总结
2、成员属性
table |
哈希数组 |
DEFAULT_INITIAL_CAPACITY |
哈希表默认容量值(16) |
MAXIMUM_CAPACITY |
最大容量值 |
DEFAULT_LOAD_FACTOR |
默认负载因子 |
threshold |
当前存放元素数量达到该值时就会触发数组扩容 |
TREEIFY_THRESHOLD |
树化条件之一(转化为红黑树的阈值) |
MIN_TREEIFY_CAPACITY |
树化条件之一(转化为红黑树的阈值) |
UNTREEIFY_THRESHOLD |
解树化条件 |
size |
哈希表中实际存储的元素个数 |
如图所示:
3、构造方法
3.1 带参构造
带两个参数的构造方法,可以指定初始容量和负载因子:
- 若指定的容量<0,则抛出异常
- 若指定的容量 >最大容量,则容量为最大容量
- 若指定的负载因子<=0,抛出异常、
- 经过 tableSizeFor 方法,threshold得到的值为指定容量的2的次幂
带一个参数的构造方法:指定初始容量,负载因子为默认值:
(内部还是调用了带两个参数的构造方法)
带一个参数的构造方法:传入集合:
3.2 无参构造
无参构造,没有再调用其他构造方法,负载因子为默认值:
(未做任何初始化,成员threshold的值为0)
到目前为止,哈希数组table还没有开辟内存,那么数组的空间是在哪里被开辟的呢?
我们猜想是在put方法中:
4、哈希数组初始空间的开辟
4.1 put方法(第一次添加元素)
根据put方法,我们进入putVal方法:
因为初始时哈希表为空,进入resize方法为数组开辟空间。
4.2 resize方法(数值初始空间的开辟)
我们发现,初始时我们为数组开辟空间的大小取决于threshold的初始值,而threshold的初始值又取决于我们所调用的构造方法:
- 若调用的是无参构造,则threshold=0
- 若调用的是带参构造,且指定了初始容量大小initialCapacity,那么threshold的值就为指定容量的2的次幂的数值
当 threshold = 0 时,哈希表开辟的初始容量为默认容量16
当 threshold != 0 时,哈希表开辟的初始容量就为threshold
我们做出以下总结:
- 当通过无参构造 构造HashMap时,第一次put为哈希表开辟的初始容量为默认容量16
- 当通过带参构造 构造HashMap时,若给出的指定初始容量为n,那么第一次put为哈希表开辟的初始容量为n的二次幂
resize方法具体分析如下图:
5、put方法
上面讲了第一次put元素,数组为空时对数组空间的开辟。
接下来讲一讲在数组空间已有的情况下,如何进行元素的添加,我们发现:
- 当n的值为2的次幂时,(n-1)&hash 与 hash%len的值是相同的,这就是为什么在构造方法时,将threshold设置为2的次幂的原因。(计算得出新元素的位置)
- 判断插入位置是否哈希冲突,如果不冲突则直接创建新节点插入。
- 判断是否与原有元素的key值相同
- 判断插入的是红黑树还是链表,是红黑树则调用putTreeVal插入到红黑树中
- 如果是链表,新元素尾插入链表(JDK17采用尾插法)
- 当链表长度>=8,进入treeifyBin函数,进一步判断是否需要树化。
- 若进入了treeifyBin方法,还需判断数组长度是否>=64,若>=64才能树化。
故,树化的条件总结:链表长度>=8 && 数组长度>=64
put方法具体分析如下:

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)