确定桶位置的计算方式是:(table.length-1) & hash,因为table.length-1的二进制全是1,在和hash作&运算时如果位数不够,就在前面补0,这时(table.length-1)的二进制中后面的1视为低位,前面的0就是高位,所以这个运算,高位算出来全是0,所以主要看低位。
现在看 e.hash & oldCap,oldCap因为时2的倍数,所以二进制都是1000……的形式,1视为高位,那么所以这个运算中,e.hash的低位不用管,高位可能是1也可能是0,所以运算的结果只能是0或1
那么为什么e.hash高位时1就要移位,是0就不需要呢?
现在已经清楚确定桶位置的计算方式是:(table.length-1) & hash,例如原本容量是16,那么(table.length-1) 二进制就是1111,hash是11111,则在原来的位置是 01111 & 11111 = 15,但是现在扩容之后为32了,现在(table.length-1)二进制为11111,那现在的位置就是11111 & 11111 = 31,改变的位置=原来的位置+原来的容量。
其实原因就是因为节点Node的hash没有变化,可是容量变了,所以如果节点Node的高位为1就是计算出与之前不一样的值,确定的位置当然要发生变化。
原来的数据存储
扩容后的数据存储
非线程安全jdk1.8中的HashMap线程不安全主要是在多线程并发的时候出现数据覆盖的情况,在putVal方法中
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { // tab是用来操作存储容器的,p是存储的节点,n是数组的长度,i是数组位置下标 Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) // 数组容器初始化resize(),这个方法后面重点看,就是重新设置容量大小 n = (tab = resize()).length; // 如果找到的这个位置的Node节点为null,就直接new一个,将put的数据存放进来 // (table.length-1)&hash 是HashMap中确定数组存放位置的方式 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);看上面的代码,if ((p = tab[i = (n - 1) & hash]) == null),如果找定位到数组的位置节点为空的话,就直接new一个节点存数据,当多线程时候,出现hash冲突,都定位到同一个位置,当一个A线程进来这个if,还没来得及存数据,另一个B线程进来抢先存了数据,可是A再去存数据的时候,已不会判断是否有值了,就直接覆盖了,所以就将B线程的数据覆盖了。