HashMap扩容机制

来源:互联网 发布:深圳策略一二三网络 编辑:程序博客网 时间:2024/05/22 10:58

通过这篇笔记 http://blog.csdn.net/aaa821/article/details/70193617 我们已经知道HashMap的数据结构是由数组+链表构成的,存储的计算公式是hash(key)%len

当Map的数据越来越大时,会进行相应的扩容,有趣的是扩容时总是长度*2 


以下内容转载自这篇博客  http://blog.csdn.net/u010558660/article/details/50926227


如果length是2的n次方,"length must be a non-zero power of 2";则下面的等式成立,
 h & (length-1)   =   h % length   等值不等效  h & (length-1)的效率高于h % length
 (因为存储是用hash(key) % length来存储的 而 h&(length-1) 比 h %length的效率高,所以想让存储时用 h & (length -1) 来替代 h % length 就要让两者变成一个等价关系,即 h & (length -1) = h % length 而确保两者为等价关系的条件就是length是2的n次方)

我们看HashMap的indexFor方法就已经用了 h & (length -1)
 按位取与,作用上相当于取模mod或者取余%。hashCode不同也可能数组下标相同。

 为什么HashMap的容量或之后的扩容,总是2的n次方?
 这看上去很简单,其实很巧妙。

 假设数组长度分别为15和16,优化后的hash码分别为8和9,那么&运算后的结果如下:


**从上面的例子中可以看出:当8、9两个数和(15-1)2=(1110)进行“&运算”的时候,产生了相同的结果,都为0100,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到数组中的同一个位置上形成链表,那么查询的时候就需要遍历这个链表,得到8或者9,这样就降低了查询的效率。

**同时,我们也可以发现,当数组长度为15的时候,hash值会与(15-1)=(1110)进行“&运算”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!

**而当数组长度为16时,即为2的n次方时,2n-1得到的二进制数的每个位上的值都为1(这是一个奇妙的世界),这使得在低位上&时,得到的和原hash的低位相同,加之hash(int h)方法对key的hashCode的进一步优化,加入了高位计算,就使得只有相同的hash值的两个值才会被放到数组中的同一个位置上形成链表。

**所以说,当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了

综上所述 2的n次方-1得到的二进制每个位上的值都为1,而非2的n次方-1得到的二进制每个位上的值有可能为0,为0的后果就是造成有些位置永远不会被存放元素,即浪费空间又增加碰撞的几率,所以基于这个原因,在扩容时才都是长度*2 


原创粉丝点击