很久以后回头再看源码,发现其初始化以及扩容过程,与之前认知有出入,记录一二。
HashMap几个重要的属性参数:
Entry[] table:HashMap底层用一个Entry对象存储一组key-vlaue键值对,table作为Entry对象的数组,存储了所有的键值对(遍历操作,多是针对table来进行)
size:HashMap的键值对的数量,也可以理解为table的长度
loadFactor:扩容因子,默认为0.75
capacity:容量,默认为16
threshold:hashmap进行扩容的阀值,一般为capacity * loadFactor
put方法主要进行如下几个操作:
1.如果是空数组,进行数组的初始化,并计算扩容阀值threshold。
2.判断key是否为空。
3.计算key的hash值,并用hash值与数组的数组table位运算,来确定存放在数组中的存放位置。
4.遍历判断是否是重复的key,如果key的hash相同且equals值相同,表示key重复,覆盖vlaue,并返回旧的value。
5.添加Entry操作,addEntry
put方法的源码结合源码写一个小例子来说明put操作和扩容的过程,以及说明达到阀值也不一定立马就扩容。给hashMap指定容量为4
第一次put操作的时候,这时的table是一个空数组,还没有进行初始化,需要进行初始化
第一次put初始化会第一次确定Entry数组table的大小,并且此方法会附带计算threshold阀值,来为后面的扩容操作做准备。
容量的确定以及阀值得计算数组的的容量分配和扩容阀值得计算完成后,接下来判断key是否为空,并相应操作。
接下来根据key的hash,将hash与table的长度进行按位&运算得到数组位置索引bucketIndex,后面会用来在此位置存放一个Entry<K,V>。
在进行插入数组之前,需要判断是否是重复的key,如果重复,覆盖原vlaue,并返回原value。
当上面的操作都通过之后,进行真正的插入操作addEntry,hash、key、value、bucketIndex,这些值在插入操作时候都要用到。
创建一个新的Entry,将其插到数组中的对应位置(采用头插法),并将table的长度size加1
其实,插入操作前,会进行容量的判断,是否达到扩容的阀值,这里需要注意,尽管已经达到阀值,但是本次确定的数组下标(或称索引)处如果未被使用,则本次操作不会进行扩容操作
举个例子,本列中初始容量为4,因子为0.75,阀值threshold=4*0.75,等于3。
当第四次进行put操作,此时的size等于3,size>=threshold成立,已经达到扩容的阀值。但是由于计算出来的bucketIndex等于2,而数组table[2]没有被使用,依旧不会进行扩容,扩容操作将会延缓到下一次操作
只有当第五次put操作时,table没有了空余位置,且size大于阀值threshold,进行扩容操作:
1.创建一个新的数组,容量是原来的两倍,作为扩容后的新数组。
2.将原数组的值迁移搬运到新的数组中。
3.重新计算扩容阀值
网友评论