HashMap容量分析

简介: 了解过HashMap都应该知道,HashMap内部会创建一个Entry table数组来存放元素,而且这个数组的长度永远都是2的指数次方。那么问题来了,为什么选择2的指数次方呢?首先,思考一下计算出hash值后,应该存放在数组的哪个位置?显然用求余(模)最简单。

了解过HashMap都应该知道,HashMap内部会创建一个Entry<K, V> table数组来存放元素,而且这个数组的长度永远都是2的指数次方。那么问题来了,为什么选择2的指数次方呢?
首先,思考一下计算出hash值后,应该存放在数组的哪个位置?显然用求余(模)最简单。然而模的效率并不高,看看JDK是怎么做的,indexFor方法:

static int indexFor(int h, int length) {
    // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
    return h & (length-1);
}

其中h就是hash值,length是table的长度。对2的指数次方求模运算(h % length)和h & (length-1)结果是一样的,这点从代码注释也可以知道,不明白可以看最后的备注。

接着看看为什么容量是2的指数次方的问题,假如容量分别是16和15两种情况,对应的length-1的二进制分别是11111110,然后我们思考hash值分别是8和9这两种情况,8 & 1111 = 8,9 & 1111 = 9,存放在table[8]和table[9]对应的位置,没有发生碰撞;然后8 & 1110 = 8,9 & 1110 = 8,两个hash值都被存放在了table的同一位置,显然发生了碰撞。放大来看,如果容量是15的话,length - 1对应的二进制是1110,进行与运算后,最后一位永远是0,即xxx0,这将会导致table中0001001101011001101101111101这几个位置永远都不能存放元素了,空间浪费不说,碰撞概率也增大。而我们2的指数次方对应的数length-1后二进制全是1,进行与运算显然不会干扰到原hash值,不会导致table中哪个位置不能存放元素。

解释一下“对2的指数次方求模运算(h % length)和h & (length-1)结果是一样的”,以4(二进制为11)为例:
0 % 4 = 0 同 4 % 4 = 0 ……
1 % 4 = 1 同 5 % 4 = 1 ……
2 % 4 = 2 同 6 % 4 = 2 ……
3 % 4 = 3 同 7 % 4 = 3 ……
00 & 11 = 0 同 100 && 11 = 0 ……
01 & 11 = 1 同 101 && 11 = 1 ……
10 & 11 = 2 同 110 && 11 = 2 ……
11 & 11 = 3 同 111 && 11 = 3 ……
明显,求余运算每4个一循环,对应二进制大于4之后,高位的数字与0进行与运算,始终为0,效果等同于每4个一循环。

总结一下:使用2的指数次方作为HashMap中存放元素数组的大小,可以避免求余操作,效率较高,同时,减少了碰撞次数。

目录
相关文章
|
3月前
|
算法 Java 开发者
为啥HashMap的默认容量是16?
为啥HashMap的默认容量是16?
41 0
|
3月前
|
Java
Java为什么建议初始化HashMap的容量大小?
【5月更文挑战第3天】Java中初始化HashMap容量能提升性能。默认容量16,扩容按当前的1/2进行。预估元素数量设定合适容量可避免频繁扩容,减少性能损耗。过大浪费内存,过小频繁扩容,需权衡。Java 8后扩容策略调整,但核心仍是预估初始容量以优化性能。
57 1
|
12月前
|
Java 索引
蚂蚁金服Java研发岗二面:说说HashMap 中的容量与扩容实现
JDK1.8 中 HashMap 的底层实现,我相信大家都能说上来个 一二,底层数据结构 数组 + 链表(或红黑树) ,源码如下
|
3月前
|
Web App开发 存储 数据可视化
VisualVM【实践 01】工具VisualVM下载使用及插件Visual GC示例说明HashMap初始化容量initialCapacity的影响(源码及visualvm_215.zip分享)
VisualVM【实践 01】工具VisualVM下载使用及插件Visual GC示例说明HashMap初始化容量initialCapacity的影响(源码及visualvm_215.zip分享)
65 0
|
9月前
|
存储 缓存 Java
Java中使用HashMap时指定初始化容量性能一定会更好吗?
可以看出,容量16是个分水岭,当容量为16时,二者几乎没啥差异,这也很容易理解,当不指定容量时默认初始容量就是16。但容量大于16时,指定容量时的性能会高于不指定时的性能,随着数量的增加,前者会比后者性能高出50%。但当数据量小于16时,不指定容量大小反而性能更高,最多甚至相差2倍,这就和我们之前的认知不一样了。
70 0
JUC学习(六):HashMap和HashSet的线程不安全问题分析和解决方案(写时复制技术、ConcurrentHashMap)
JUC学习(六):HashMap和HashSet的线程不安全问题分析和解决方案(写时复制技术、ConcurrentHashMap)
JUC学习(六):HashMap和HashSet的线程不安全问题分析和解决方案(写时复制技术、ConcurrentHashMap)
|
Java 索引
81. 说说HashMap 中的容量与扩容实现
81. 说说HashMap 中的容量与扩容实现
70 0
81. 说说HashMap 中的容量与扩容实现
|
消息中间件 Java Kafka
【从Java面试题看源码】-HashMap 初始容量 计算方法
【从Java面试题看源码】-HashMap 初始容量 计算方法
【从Java面试题看源码】-HashMap 初始容量 计算方法
HashMap 使用的时候指定容量?你真的用明白了吗?(值得一阅)
HashMap 使用的时候指定容量?你真的用明白了吗?(值得一阅)
269 0
HashMap 使用的时候指定容量?你真的用明白了吗?(值得一阅)
分析HashMap 的 JDK 源码
分析HashMap 的 JDK 源码
分析HashMap 的 JDK 源码