HashMap是一个实现Map接口Java集合框架。HashMap的最重要特征是它具有恒定的时间性能以进行检索和插入, 决定HashMap性能的两个因素是:
- Initial Capacity(初始化容量)
- Load Factory(负载系数)
在我们解释HashMap的Initial Capacity和Load Factory是什么之前,了解它的结构是至关重要的。
HashMap的节点包含键-值对,就像映射中的节点一样。HashMap有包含节点的bucket,一个bucket可以包含多个节点。HashMap的基本结构如下:
index:对键的散列值和数组大小减1进行位与运算后得到的整数值。
Index=hashcode(key)&(ArraySize–1)
其中hashcode是一个预定义函数,它返回键哈希的整数值,ArraySize是HashMap中的bucket数。
bucket:它是节点的LinkedList结构。
node:它是哈希映射的基本单位。它包含键值对和到下一个节点的链接。
声明HashMap对象的语法如下:
HashMap objectName=new HashMap(int initialCapacity,float loadFactor)
initial Capacity(初始化容量)
初始容量本质上是HashMap中的bucket数,默认情况下是16. 一个好的HashMap算法会将相等数量的元素分配给所有的bucket
假设我们有16个元素,那么每个bucket将有1个节点,任何元素的搜索都将通过1个查找来实现。如果我们有32个元素,那么每个bucket将有2个节点,任何元素的搜索都将通过2次查找来实现。类似地,如果我们有64个元素,那么每个bucket将有4个节点,任何元素的搜索都将通过4次查找来实现,依此类推
正如您所观察到的,当元素的数量将查找增量的数量增加一倍时,这对性能是有好处的。
但是当元素的数量达到一个很高的值,比如10000,我们需要625个查找。同样,如果元素的数量是1000000,我们将需要62500个查找。如此高的查找次数将降低HashMap的性能。这就是负载系数发挥作用的地方。
Load Factory(负载系数)
负载系数是一个阈值,如果当前所有元素与初始容量之比超过该阈值,那么容量将增加,因此HashMap的操作复杂性保持为O(1),O(1)的操作复杂度的含义是指检索和插入操作需要恒定的时间。
HashMap的默认负载系数是0.75
我们如何决定何时增加容量?
让我们举个例子,因为初始容量默认为16,所以现在我们有16个存储桶。
我们插入第1个元素,负载系数将为1/16=0.0625。检查是否为0.0625>0.75?答案是否定的,所以我们不增加容量。
接下来我们插入第2个元素,当前负载系数将为2/16=0.125。检查值是0.125>0.75?答案是否定的,所以我们不增加容量。
同样,对于第3个元素,负载系数=3/16=0.1875不大于0.75,所以我们不增加容量。
第4个元素,负载系数=4/16=0.25不大于0.75,所以我们不增加容量。
第5个元素,负载载系数=5/16=0.3125不大于0.75,所以我们不增加容量。
第6个元素,负载系数=6/16=0.375不大于0.75,所以我们不增加容量。
第7个元素,负载系数=7/16=0.4375不大于0.75,所以我们不增加容量。
第8个元素,负载系数=8/16=0.5不大于0.75,所以我们不增加容量。
第8个元素,负载系数=8/16=0.5不大于0.75,所以我们不增加容量。
第9个元素,负载系数=9/16=0.5625不大于0.75,所以我们不增加容量。
第10个元素,负载系数=10/16=0.625不大于0.75,所以我们不增加容量。
第11单元,负载系数=11/16=0.6875不大于0.75,所以我们不增加容量。
第12个构件,负载系数=12/16=0.75等于0.75,所以我们不增加容量。
第13个构件,荷负载系数=13/16=0.8125大于0.75,插入第13个元素时,HashMap会扩容原来的两倍。
现在容量是32。
类似地,每次插入超过负载系数0.75时,在get(){retrieval}和put(){insertion}操作的恒定时间性能下,容量将加倍。