开始

首先看HashMap中默认初始容量的源码.

/**
 * The default initial capacity - MUST be a power of two.
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

源码选自 AdoptOpenJdk 11.0.2+9

正文

我们看变量名 DEFAULT_INITIAL_CAPACITY,说明这确实是HashMap初始化的默认容量,但是说好的16呢. 读者:“你莫不是在骗我”。 别急,且看 变量定义的时候使用了 1 << 4 这1和4都认识,<<是个什么鬼,一些基础不扎实的可能就忘了.

<<: 左移运算符(和 +-*/ 是一辈的),它是这么定义的:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

首先 1 = 00000000000000000000000000000001(4字节,32位)
左移一位变成 00000000000000000000000000000010
再移一位变成 00000000000000000000000000000100
再移一位变成 00000000000000000000000000001000
再移一位变成 00000000000000000000000000010000
这就是16不用解释了吧.

问题

那么问题来了,HashMap的作者为什么要这么定义,炫技吧.

遇代码不懂看注释,注释我一并带过来了.这里用我匮乏的词汇量翻译一下: 默认初始化容量 - 必须是2的次方.

看一些解释文章,都说HashMap每次扩容都是2的整数次方(原因我们在下一篇文章中探讨一下),那么初始化也不能例外,也得是2的整数次方.

定义一个数是2的n次方,有什么方法?

二逼青年: 2222…(n个) 普通青年: 2^n 文艺青年: 1 << n

前两个我们都能看懂,最后一个怎么说,我们还是看刚才的16

00000000000000000000000000000001 = 1    1
00000000000000000000000000000010 = 2    1左移1位
00000000000000000000000000000100 = 4    1左移2位
00000000000000000000000000001000 = 8    1左移3位
00000000000000000000000000010000 = 16   1左移4位

看吧,2的n次方 等于 1左移n位,有趣的小知识又增加了

还是没说JDK为什么要这么定义.

上边说了,HashMap在扩容的时候,容量都是2的整数次方,那么我们完全可以用当前的容量左移一位实现呀,不会更麻烦,性能却更高(移位算法直接操作二进制比乘法性能高的多),而且看起来逼格更高还能炫技(炫技肯定是次要的啦,主要还是性能,JDK的源码简直不要太喜欢移位算法).

我猜测作者大概认为,既然之后定义2的次方要使用移位算法,不如索性初始化容量也用移位定义吧,还自带语义(看见了吧,注释就是给我们这些菜鸟用的,大佬可以直接知道这就是2的4次,注释完全就是多余的).