精妙的位移运算
初始化槽位大小,返回比cap大的最小2的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;
}
2的次方数特点 只有一个bit位是1 其余都是0 eg : 0000 1000 -> 8
以9为例走一遍 9变16====================================开始
1. 9的二进制表示 0000 0000 0000 1001
2. 减1后 ,n = 0000 1000 (高位都是0 忽略掉)
3. 右移一位 0000 0100
4.或运算 0000 1000 >> 0000 1100
5.右移两位 0000 0011
6.或运算 0000 1100 >> 0000 1111
7.右移四位 0000 0000
8.或运算 0000 1111 >> 0000 1111
9.右移八位 0000 0000
10. 跳过一些 直接到右移动16位或运算 n此时低四位都是1
11.n+1 0001 0000 => 16
9变16====================================结束
走了这么多,总结下就是把n-1的二进制的 高位是1开始后面的值全部设置成0(含第一个高位是1的那位),高位往前一位设置成1
减1是为了兼容本身就是2的N次方的数
看下5到8 9到16可以清晰看出来
5 0000 0101
8 0000 1000
9 0000 1001
16 0001 0000
原始的计算槽位
(n-1)& hash n是数组的长度 (2的次方数 ), hash是key的经过处理的hash(处理看下面那一小段)
这个与运算比较简单了 容量是16位的数组 n-1 就是 0000 0000 0000 1111 与出来的值最多是15.管你是啥 高位全部归零
简单说下对这个hash的处理
(h=key.hashCode()) ^ (h>>>16)
h右移16位 主要让高位右移 减少hash碰撞[想下 如果低位都是0 那效果估计很糟糕 疯狂碰撞]
rehash计算槽位
newTab[e.hash & (newCap - 1)] = e;
具体自己画图领悟吧,说下结论 扩容是oldCap<<1 即容量扩大了一倍
老元素的key.hash&操作后 ,两种情况【自己要看原来的hash在扩容的哪一位上是不是1】
1.老的key的低N位如果是0那还是在老的槽位,
2.低N位如果是1 新坐标= 老坐标 + OldCap
举个通俗的例子 默认大小是16个槽位,扩容后变成32个槽位
假设x的hash是(0000 0010)在老数组的2个位置,扩容散列后 它不受影响还是在第二个位置 (他没有潜力 他的高位全是0)
y的hash是 0001 1111 原来他的index是15 扩容后它到了31个
0000 1111 (这个是16位的cap-1)
0001 1111 (这个是32位的cap-1)
扩容机制
前置
hashMap的所有元素量>=扩容阀值&数组位置已经有值 1.7的逻辑,1.8里面把第二个条件移除
具体骚操作
jdk1.7 主要是正常插入的时候是尾插,rehash的时候是头插
jdk1.0 都是尾插,具体代码如下 用了两个链表
Node<String,String> loHead = null, loTail = null;
Node<String,String> hiHead = null, hiTail = null;
Node<String,String> next;
do {
next = e.next;
System.out.println("hash-" + e.hash + "& oldCap" + (e.hash & oldCap));
System.out.println(oldCap);
// 1111001100000001110101110000
// 0000000000000000000000001000
//低位是0的 用loHead装配
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
fail-fast细节
for循环会检测modCount和exceptedModCount,每次变更都会增加modCount 不一致就会报错