本文主要介绍,HashMap源码解析中的一小部分(JDK 1.8版本)。 构造函数里使用的tableSizeFor函数
从这个构造函数说起——Map
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
这源码中的构造函数不复杂,先看看里面的 tableSizeFor 函数
/**
* Returns a power of two size for the given target capacity.
* 上面那句英文翻译下,大概意思是:返回指定容量的 二的n次方
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
我第一眼看上去,很晕,相信我并不孤独,大多数人看起来都晕。
先说下这个函数的效果。输入3、4、5 都返回 8。 输入 11、12、13都返回16,输入200、210、220都返回256。
即输入值为 x,返回值 y 是 : 满足 y = 2k >= x,k取最小值时。
这个看明白之后,咱解释下源码是怎么实现的。分两步讲
第一步: ---- 右移位
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
这一段的效果,一个 int 型,不包括符号位,1之后的0,全部改成1。比如 n=129
int n = 129;
System.out.println("n: " + Integer.toBinaryString(n));
int x = n >>> 1;
System.out.println("x: " + Integer.toBinaryString(x));
n |= n >>> 1;
System.out.println("n |= n >>> 1 :" + Integer.toBinaryString(n));
n |= n >>> 2;
System.out.println("n |= n >>> 2 :" + Integer.toBinaryString(n ));
n |= n >>> 4;
System.out.println("n |= n >>> 4 :" + Integer.toBinaryString(n));
n |= n >>> 8;
System.out.println("n |= n >>> 8 :" + Integer.toBinaryString(n));
n |= n >>> 16;
System.out.println("n |= n >>> 16 :" + Integer.toBinaryString(n));
-----------------------运行结果------------------------------------------
n: 10000001
x: 1000000
n |= n >>> 1 :11000001
n |= n >>> 2 :11110001
n |= n >>> 4 :11111111
n |= n >>> 8 :11111111
n |= n >>> 16 :11111111
1000 0001 ---- 129 的二进制
0100 0000 ---- 右移1位:
1100 0001 ---- “|”运算结果
“|”运算的定义就是,只要有其中一个是1,结果就是1。
对于129来说,对于二进制的第一个1,经过n |= n >>> 1 后,第一个1后面那一位,一定变成1。(这句不懂,把上面的例子再看看。),即有两个1了。
经过n |= n >>> 2 之后,刚刚说的那两个1 就变成 4个1了,依次类推。
int 型,总共 32位,经过这 n |= n >>> 1 到 n |= n >>> 16 之后,肯定可以达到这样的结果,从第一个 1之后,所有位上的数字都会变成1。
第二步 ---- 开头的减 1 与最后的加1;
int n = cap - 1;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
如果 cap = 2k 那减 1 之后,有效的二进制位,会减小一位,比如 8 的二进制是1000 , 8 -1 = 7,7的二进制是 0111。经过中间的几步后,最后加1,那还是等于8。
即输入 2k 输出 2k
如果 2k > cap > 2k-1 , cap 减 1 之后,有效的二进制位不变,经过中间几步,所有二进制位都变成1,最后加 1 ,二进制位会多一位.。
比如上面提到的 129,进制是:1000 0001 ,经过中间几步,就变成 1111 1111,
加1,就变成 1 0000 0000 即 28 = 256
即输入cap,输出 2k
到此,tableSizeFor 函数就解析完了,如果还有些不清楚,那在代码中代入一个数字,跑几次,再想想。这个函数,代码不好理解,能写出这代码的,一定是大神。