数据结构(Java):HashMap源码解析【JDK17】

1、整体总结


 

2、成员属性

table
哈希数组
DEFAULT_INITIAL_CAPACITY
哈希表默认容量值(16)
MAXIMUM_CAPACITY
最大容量值
DEFAULT_LOAD_FACTOR
默认负载因子
threshold
当前存放元素数量达到该值时就会触发数组扩容
TREEIFY_THRESHOLD
树化条件之一(转化为红黑树的阈值)
MIN_TREEIFY_CAPACITY
树化条件之一(转化为红黑树的阈值)
UNTREEIFY_THRESHOLD
解树化条件
size
哈希表中实际存储的元素个数

如图所示: 

3、构造方法

3.1 带参构造

带两个参数的构造方法,可以指定初始容量和负载因子

  1. 若指定的容量<0,则抛出异常 
  2. 若指定的容量 >最大容量,则容量为最大容量
  3. 若指定的负载因子<=0,抛出异常、
  4. 经过 tableSizeFor 方法,threshold得到的值为指定容量的2的次幂


带一个参数的构造方法:指定初始容量,负载因子为默认值:

(内部还是调用了带两个参数的构造方法)


带一个参数的构造方法:传入集合:


3.2 无参构造

无参构造,没有再调用其他构造方法,负载因子为默认值:

未做任何初始化,成员threshold的值为0) 


 到目前为止,哈希数组table还没有开辟内存,那么数组的空间是在哪里被开辟的呢?

我们猜想是在put方法中:


4、哈希数组初始空间的开辟

4.1 put方法(第一次添加元素)

 根据put方法,我们进入putVal方法:

因为初始时哈希表为空,进入resize方法为数组开辟空间。

4.2 resize方法(数值初始空间的开辟)

我们发现,初始时我们为数组开辟空间的大小取决于threshold的初始值,而threshold的初始值又取决于我们所调用的构造方法:

  1. 若调用的是无参构造,则threshold=0
  2. 若调用的是带参构造,且指定了初始容量大小initialCapacity,那么threshold的值就为指定容量的2的次幂的数值

 当 threshold = 0 时,哈希表开辟的初始容量为默认容量16

当 threshold != 0 时,哈希表开辟的初始容量就为threshold

我们做出以下总结:

  1. 当通过无参构造 构造HashMap时,第一次put为哈希表开辟的初始容量为默认容量16
  2. 当通过带参构造 构造HashMap时,若给出的指定初始容量为n,那么第一次put为哈希表开辟的初始容量为n的二次幂

 resize方法具体分析如下图:


5、put方法

上面讲了第一次put元素,数组为空时对数组空间的开辟。

接下来讲一讲在数组空间已有的情况下,如何进行元素的添加,我们发现:

  1. 当n的值为2的次幂时,(n-1)&hash 与 hash%len的值是相同的,这就是为什么在构造方法时,将threshold设置为2的次幂的原因。(计算得出新元素的位置)
  2. 判断插入位置是否哈希冲突,如果不冲突则直接创建新节点插入。
  3. 判断是否与原有元素的key值相同
  4. 判断插入的是红黑树还是链表,是红黑树则调用putTreeVal插入到红黑树中
  5. 如果是链表,新元素尾插入链表(JDK17采用尾插法)
  6. 当链表长度>=8,进入treeifyBin函数,进一步判断是否需要树化。
  7. 若进入了treeifyBin方法,还需判断数组长度是否>=64,若>=64才能树化。

故,树化的条件总结:链表长度>=8 && 数组长度>=64

put方法具体分析如下:

 

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值