HashMap扩容中,hash & oldCap的作用,观察扩容前和扩容后下标的变化
原来的0101和10101在length=16的时候,通过hash&length-1的方法,计算出来都是0101;但是在扩容后即length=32时,hash&length -1 的方法计算出来为0101和10101;这就说明扩容后有些数据需要移动到原来index+oldcap的位置;为了观察0101和10101的区别发现只有最高位是0和1的区别;为了避免进行重复的hash&length -1计算,此时采用了hash&oldCap的方法,将10101和10000;也就是取最高位的值;来判断是否需要移动位置,节省时间。
1.7的hashMap扩容成环问题
void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; //下面这段代码的意思是: // 从OldTable里摘一个元素出来,然后放到NewTable中 for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } } |
上述的while循环中,线程2对newTab的操作是会对线程1产生影响的
在1.8中,具体如下:
Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; 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; } |
线程1执行到next=e.next,中断;然后线程2开始执行。
当线程2执行完毕后,此时的hiHead与loHead属于局部变量,也就是线程2对其做的操作对线程1没有影响,并且该段代码在循环体中没有对newTab进行操作。
线程1此时恢复执行,hiHead与loHead都恢复成空,即重新执行一遍循环体,在进行newTable的赋值,会覆盖掉上次的结果,不过结果是一样的。
但是1.8也存在不安全的问题,在进行添加元素和删除元素的时候,如果该元素不存在,会进行size++;
此时如果两个线程同时执行,按理说size=3;但实际上会出现2;线程之间只对自己的副本进行操作,所以会引起这个问题。
HashMap扩容中,hash & oldCap的作用,观察扩容前和扩容后下标的变化
原来的0101和10101在length=16的时候,通过hash&length-1的方法,计算出来都是0101;但是在扩容后即length=32时,hash&length -1 的方法计算出来为0101和10101;这就说明扩容后有些数据需要移动到原来index+oldcap的位置;为了观察0101和10101的区别发现只有最高位是0和1的区别;为了避免进行重复的hash&length -1计算,此时采用了hash&oldCap的方法,将10101和10000;也就是取最高位的值;来判断是否需要移动位置,节省时间。
1.7的hashMap扩容成环问题
void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; //下面这段代码的意思是: // 从OldTable里摘一个元素出来,然后放到NewTable中 for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } } |
上述的while循环中,线程2对newTab的操作是会对线程1产生影响的
在1.8中,具体如下:
Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; 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; } |
线程1执行到next=e.next,中断;然后线程2开始执行。
当线程2执行完毕后,此时的hiHead与loHead属于局部变量,也就是线程2对其做的操作对线程1没有影响,并且该段代码在循环体中没有对newTab进行操作。
线程1此时恢复执行,hiHead与loHead都恢复成空,即重新执行一遍循环体,在进行newTable的赋值,会覆盖掉上次的结果,不过结果是一样的。
但是1.8也存在不安全的问题,在进行添加元素和删除元素的时候,如果该元素不存在,会进行size++;
此时如果两个线程同时执行,按理说size=3;但实际上会出现2;线程之间只对自己的副本进行操作,所以会引起这个问题。