源码分析为什么HashMap的table长度一定是2的整次幂

我们今天从源码入手分析一下为什么HashMap的数组table长度一定是2的整次幂。

首先我们先从构造方法来分析HashMap的初始化长度:

HashMap中有4个构造方法
在这里插入图片描述
我们来看看他们分别都是什么

1 . HashMap();

在这里插入图片描述
无参的构造函数,从注释中我们可以看出,如果我们不指定初始长度,那么数组的初始长度就是默认的长度,是一个静态常量DEFAULT_INITIAL_CAPACITY = 16;
emmm确实是2的4次幂,但是不能说明任何问题;我们继续看

2 . HashMap(int initialCapacity)

在这里插入图片描述
诶,我们找到了了一个有一个参数的构造方法,并且这个参数就是用来指定我们数组的初始化长度的,那么我们如果给它一个不是2的整次幂的值,它是咋办的呢,因为这个方法调用了另外一个方法,我们想知道还得看一下这个里面的构造方法,也就是第三个构造方法。

3 . HashMap (int initialCapacity, float loadFactor);

在这里插入图片描述
终于有了比较有价值的代码了,我们抛开前面的判断,我们最后的目光就落在了最后一个方法
this.threshold = tableSizeFor(initialCapacity);
注意:虽然此处是对threshold赋值,但是后面table初始化时会将值赋给table长度,并且重新对threshold进行赋值为 capacity * loadFactory,这里不多赘述。

这个方法最后对我们传入的初始长度值进行了加工。再来看一下这个方法
在这里插入图片描述
其实从注释我们就已经看的很清楚了,方法返回一个两倍大小的目标容量的幂,诶
但是我们还是看一下代码的实现,就是“与”运算一连串的无符号右位移,我们随便代入一个值 14
n = 14 - 1 = 13

13的2进制为
0000 0000 0000 0000 0000 0000 0000 1101
计算n = n | n >>> 1
0000 0000 0000 0000 0000 0000 0000 1101
				|
0000 0000 0000 0000 0000 0000 0000 0110	
结果为
0000 0000 0000 0000 0000 0000 0000 1111

然后用这个结果 = n继续与它右位移后的值进行“或”运算,但是想在我们就发现,不管后面再计算几次,结果已经不会变了,永远是1111,所以最后n的结果为15。
我们再代入到最后的三目运算符中,n小于最大的容量,所以最终n的结果为16

诶,有点意思,但其实我们发现,这个运算是返回一个大于它且是最近的2的整次幂的数。
有点绕,我们换个角度
在这里插入图片描述
分成一个个的小区间,比如 16 < n < 32 的数就会返回32
因为 (2的整次幂 - 1) 的2进制有效位一定都是1

4 . HashMap(Map<? extends K, ? extends V> m)

在这里插入图片描述
第四个构造方法是将制定的Map对象实例化成一个HashMap对象,与我们讨论的问题关系不大。

至此,我们已经明白了构造方法的初始值处理。但是我们知道,数组的长度是会改变的,当我们再map中存储的元素超过threshold时,就会触发扩容机制,我们的数组长度进行扩容,我们来看一下扩容方法
在这里插入图片描述
resize()方法我们这里就不过多的分析,我们只看table数组长度变化的时候,我们从源代码中可以清晰的看到,新数组长度是旧数组长度的一倍。是2倍的扩容机制。

至此我们就从源代码中了解了为什么HashMap中的数组长度一定是2的整次幂了。如果有不对的地方希望大家指正!

还有一些问题,比如数组长度是2的整次幂的好处,以及HashMap的源码分析请看我的其他文章。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页