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方法具体分析如下:
标签:Java,HashMap,容量,数组,构造方法,源码,哈希,threshold,初始 From: https://blog.csdn.net/2401_83595513/article/details/140693093