/**
* 构造一个空的具有指定的初始容量和负载因子的 HashMap
* initialCapacity 初始容量
* loadFactor 负载因子
* threshold 下一个调整大小的值(容量*加载因子)
*/
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;
//这里调用了tableSizeFor方法,目的就是求不小于给定的初始容量的最小二次幂
//二次幂是HashMap 中的初始容量规定了必须是2的幂次
this.threshold = tableSizeFor(initialCapacity);
}
然后再来看看tableSizeFor这个方法
/**
* 返回大于等于initialCapacity的最小的二次幂数值
*/
static final int tableSizeFor(int cap) {
//这里默认减一是为了防止给的初始容量为2的幂数,最后返回比它的二次幂数大一倍的数
//首先知道 >>>表示首先转成2进制数,然后无符号右移,高位取0
|= 表示进行 或 运算后再把运算结果赋值给 n
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;
}
接下来就分析为什么要进行无符号右移的这一系列操作,举个例子通过表格来看下吧
右移位数 | 初始值 | 右移值 | 或运算后 |
---|---|---|---|
1 | 0000001x xxxxxxxx xxxxxxxx xxxxxxxx | 00000001 xxxxxxxx xxxxxxxx xxxxxxxx | 00000011 xxxxxxxx xxxxxxxx xxxxxxxx |
2 | 00000011 xxxxxxxx xxxxxxxx xxxxxxxx | 00000000 11xxxxxx xxxxxxxx xxxxxxxx | 00000011 11xxxxxx xxxxxxxx xxxxxxxx |
4 | 00000011 11xxxxxx xxxxxxxx xxxxxxxx | 00000000 001111xx xxxxxxxx xxxxxxxx | 00000011 111111xx xxxxxxxx xxxxxxxx |
8 | 00000011 111111xx xxxxxxxx xxxxxxxx | 00000000 00000011 111111xx xxxxxxxx | 00000011 11111111 111111xx xxxxxxxx |
16 | 00000011 11111111 111111xx xxxxxxxx | 00000000 00000000 00000011 11111111 | 00000011 11111111 11111111 11111111 |
进行了这五次位或运算后发现了本质就是除了最高位,给低位全部赋值给1.
这样找比这个二机制数大一点的数目的就差一步了,
所以最后返回的就是+1,
就达到找到比这个数大或者等于的最小符合要求的二进制数作为容量.
至于减1也就能理解了,如果不减去1,这个数的最低位都是1,再加上1,肯定是原来的两倍了,这就不符合要求了.