HashMap 初始化容量 1 2 // 默认容量 16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16
为什么必须是2的n次幂 因为只有是2n,才可以通过 hash & (leng-1) 计算出的索引尽可能保证数据分布均匀.
如果不是2的n次幂,计算出的索引特别容易相同,很容易发生hash碰撞,导致其余数组空间很大程度上没有存储数据,链表或者红黑树过长,效率较低.
说明:为什么是2的n次方? 2的n次幂的二进制是一个首位是1 后面为是0的数,如 2的3次方 二进制为00001000 2的3次方-1 二进制为00000111
hash & (2n-1) 表示 hash值对应的二进制与n个1 做与运算 都为1 则为1 否则为0
只有当 一个数的二进制有效位全是1的情况下 ,不同hash值计算的结果差异性才会更多。
如: 以下结果很明显全为1的情况计算的差异性会更大
1 2 3 4 1111111 100000 & 001001 &001001 ————————————————————————— 001001 000000
如果在实例化的时候传入的值不是2的n次方会怎样? 当实例化HashMap实例时,如果给定number不是2的n次方 initialCapacity(number),由于HashMap的capacity必须是2的次幂,会通过以下方法 (tableSizeFor ) 计算找到大于等于number最小的2n的数。
1 2 3 4 5 6 7 8 9 10 11 12 计算容量分析 HashMap map = new HashMap<>(10 )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 ; }
// 代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 cap - 1 的操作是为了规避如果传入的cap值本身就是2的n次方类型的数时的计算结果 cap = 10; // 计算过程(二进制) n = cap-1; 0000 1001 ——————————————————————————————————————————————— n |= n>>>1; 0000 1101 | 0000 0100 ———————————————————— 0000 1101 ——————————————————————————————————————————————— n |= n>>>2; 0000 1101 | 0000 0011 ———————————————————— 0000 1111 ——————————————————————————————————————————————— n |= n>>>4; 0000 1111 | 0000 0000 ———————————————————— 0000 1111 ——————————————————————————————————————————————— ....... 对于 0000 1111 这个数右移8位和右移16位都已经没有任何意义,最终结果都是0000 1111 ——————————————————————————————————————————————— n = n + 1 0000 1111 + 0000 0001 ———————————————————— 0001 0000 = 16
结论 以上一系列或运算的目的就是将 cap - 1 的二进制有效位全部变成1
最后再用这些有效位是1的二进制+1 ,就会变成一个大于等于传入容量的最小2的n次幂的数
注:容量最大也就是32bit的正数,因此最后n|n>>>16,最多也是32个1(这是个负数,在执行tableSizeFor之前做了判断,如果大于最大的则取最大值)
1 2 3 4 5 6 7 8 9 10 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); }