从hashMap的构造函数开始:
initialCapacity:初始化容量,默认16
loadFactor:装载因子,默认0.75
构造函数的最后以initialCapacity做入参调用了tableSizeFor方法,来看下这个方法的作用:
返回给定目标容量的二次幂,也就是返回比入参大的最小的2的n次幂。
例如:传入8,返回8;传入9,返回16.
其中,|=为或等运算符,>>>为无符号右移,源码写这么高级为了速度?
由tableSizeFor得到threshold:扩容的阈值,下面看一下它的注释
要调整大小的下一个size值,capacity * load factor 即容量*装载因子。
例如:当前map的容量为16,则map的size达到16*0.75=12时,即需要扩容。
到这时,构造函数已经执行完了,心中仍然有两个疑问,一是hashmap的容量capacity并没有被初始化,整个构造函数仅仅使用用户传入的initialCapacity定义了threshold的值。二就是threshold并不是如注释中所写的capacity * load factor。
我们反射调用看一下map的容量:
为什么,我们传入的3,而hashmap最终的容量是4呢?看一下capacity方法:
最终返回的还是threshold,大于入参的最小的2的n次幂。
个人见解:hashmap的容量在初始化时就是我们实际传入的值,并没有运算后设置为2的次幂,真正的设置容量发生在put操作时。(每个jdk版本的实现存在差异,我大学时期看1.6版本的集合源码,现在看1.8版本时发现的,但是这里不重要)
结合着第二点疑问,threshold什么时候和容量与装载因子关联起来的?我们来看一下put操作发生了哪些事:
当table==null时,执行了resize()方法,这个就是扩容的方法。
为什么这时要扩容呢?看一下里面做了什么操作:
这一系列标红的if-else操作,最终致使threshold=threshold*loadFactor,注意理清思路:在执行第一次resize()前,threshold仍然是构造函数中的比入参大的最小的2的n次幂,到这时threshold才真正担当起它扩容阈值的职责(致谢王泽龙同学的友情指导)
到这里,整个hashmap的初始化阶段已经梳理透彻,接着你对它crud时就回到上上一张图的putVal()方法,++size>threshold时,resize()。