此时,在这个特定的bucket/bin中存储的条目呈“Linked”方式存储——因此具有确切的顺序,在这个问题的上下文中非常重要。
通常,resize也可以从其他地方调用,但我们只看这种情况。
假设您将这些字符串作为键放入HashMap中(右侧是HashMap#hash之后的哈希码——这是内部再哈希)是精心生成的而不是随机的。
DFHXR - 11111
YSXFJ - 01111
TUDDY - 11111
AXVUH - 01111
RUTWZ - 11111
DEDUC - 01111
WFCVW - 11111
ZETCU - 01111
GCVUR - 11111
这里有一个简单的规律 - 所有密钥的最后4位都相同 - 这意味着当我们插入8个这样的密钥时(总共有9个),它们将会进入同一个桶中;在第9次HashMap#put操作时,将调用resize方法。因此,如果当前在HashMap中有8个条目(其中一个是上述密钥之一),这意味着该地图中有16个桶,并且密钥的最后4位决定了条目的位置。
我们放置第九个键。此时击中了TREEIFY_THRESHOLD,并调用resize。桶加倍为32,并且密钥中的另一个位决定了该条目的位置(现在有5位)。
最终到达这段代码(当发生resize时):
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;
}
其实并不那么复杂……它所做的是将当前的垃圾桶分成两部分,一部分将要移动到其他垃圾桶中,另一部分将不会移动到其他垃圾桶中——但它们肯定会留在这个垃圾桶里。
而它实现这个功能的方法非常聪明——就是通过这段代码:
if ((e.hash & oldCap) == 0)
这段代码的作用是检查接下来的位(在我们的例子中是第5位)是否为零 - 如果是,那么这个条目将保持原地; 如果不是,则会以2的幂次方偏移量向新的bin移动。
现在最后的问题是:resize中的这段代码被仔细制作,以便 保留了该bin原有的条目顺序。
因此,在您将这9个键放入HashMap之后,顺序将如下:
DFHXR -> TUDDY -> RUTWZ -> WFCVW -> GCVUR (one bin)
YSXFJ -> AXVUH -> DEDUC -> ZETCU (another bin)
HashMap
。 - EugenenewTab[e.hash & (n-1)]
始终在数组边界内的情况下。 - Holger