[英]Why HashMap traverses all nodes in linkedList rather than only the head in resize()?
The code in hashmap of jdk8 when resize is jdk8的hashmap中resize时的代码为
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
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;
}
}
}
}
}
return newTab;
}
and this part of code和这部分代码
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;
}
}
seems only traverse each bin of old table and finally assign the head to new table.似乎只遍历旧表的每个 bin,最后将头部分配给新表。 Since these assignment e = oldTab[j] and loHead = e and newTab[j] = loHead already map the old bins to new bins in map, what dose this while loop actually affect?由于这些分配 e = oldTab[j] 和 loHead = e 和 newTab[j] = loHead 已经 map 旧 bin 到 map 中的新 bin,这个 while 循环实际影响了什么? I mean the LinkedList shoule be already linked in old table, however this while loop seems want to linked it once again.我的意思是 LinkedList 应该已经链接在旧表中,但是这个 while 循环似乎想再次链接它。 Thanks for any comment!感谢您的任何评论!
The HashMap
maps hash values to bins by using a modulo table size operation (by calculating hash & (table.length-1)
, see for example https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/HashMap.java#l568 and the following line). The HashMap
maps hash values to bins by using a modulo table size operation (by calculating hash & (table.length-1)
, see for example https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/ 687fd7c7986d/src/share/classes/java/util/HashMap.java#l568和以下行)。
That means that for the table of size 16 the following hash values map to the same bin (2):这意味着对于大小为 16 的表,以下 hash 值 map 到相同的 bin (2):
Now if the table size is doubled (now 32), only these hash values map to the old bin 2:现在如果表大小加倍(现在是 32),则只有这些 hash 值 map 到旧 bin 2:
but the following hash values now map to another bin (18):但以下 hash 值现在 map 到另一个箱(18):
That means that during the resize()
operation the HashMap
has to split the linked list of entries into two:这意味着在resize()
操作期间HashMap
必须将条目的链接列表分成两个:
loHead
)那些具有 hash 值仍然映射到旧 bin 的那些(它们被添加到loHead
的链表中)hiHead
)具有映射到新 bin 的 hash 值的那些(添加到hiHead
的链表中) This splitting is done in the do {} while ();
这种拆分是在do {} while ();
中完成的。 loop that you show.你展示的循环。
e.hash & oldCap
returns 0 ( (2 & 16) == 0
and (34 & 16) == 0
).对于 hash 值保持在同一个 bin 中, e.hash & oldCap
返回 0( (2 & 16) == 0
和(34 & 16) == 0
)。e.hash & oldCap
returns 16 ( (18 & 16) == 16
and (50 & 16) == 16
)对于 hash 值被移动到新的 bin, e.hash & oldCap
返回 16 ( (18 & 16) == 16
和(50 & 16) == 16
)After splitting the list, the splitted lists are added to the new table:拆分列表后,将拆分后的列表添加到新表中:
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
oldCap
is the old table size (16 in my example). oldCap
是旧表大小(在我的示例中为 16)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.