简体   繁体   English

为什么HashMap会遍历linkedList中的所有节点而不是resize()中的head?

[英]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):

  • 2 2
  • 18 18
  • 34 34
  • 50 50

Now if the table size is doubled (now 32), only these hash values map to the old bin 2:现在如果表大小加倍(现在是 32),则只有这些 hash 值 map 到旧 bin 2:

  • 2 2
  • 34 34

but the following hash values now map to another bin (18):但以下 hash 值现在 map 到另一个箱(18):

  • 18 18
  • 50 50

That means that during the resize() operation the HashMap has to split the linked list of entries into two:这意味着在resize()操作期间HashMap必须将条目的链接列表分成两个:

  • those with a hash value that still maps to the old bin (which are added to a linked list at loHead )那些具有 hash 值仍然映射到旧 bin 的那些(它们被添加到loHead的链表中)
  • those with a hash value that maps to a new bin (which are added to a linked list at hiHead )具有映射到新 bin 的 hash 值的那些(添加到hiHead的链表中)

This splitting is done in the do {} while ();这种拆分是在do {} while ();中完成的。 loop that you show.你展示的循环。

  • for the hash values to remain at the same bin, e.hash & oldCap returns 0 ( (2 & 16) == 0 and (34 & 16) == 0 ).对于 hash 值保持在同一个 bin 中, e.hash & oldCap返回 0( (2 & 16) == 0(34 & 16) == 0 )。
  • for the hash values to are moved to the new bin, 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.

相关问题 LinkedList 中除了头部或尾部以外的节点可以为空吗? - Can nodes other than the head or the tail be null in a LinkedList? 为什么HashMap#get返回一个对象而不是一个字符串? - Why is HashMap#get Returning an Object Rather than a String? 如何在我的 HashMap 而不是整个 HashMap 中找到仅某些整体的最高值? - How can I find the highest value of only certain entires in my HashMap rather than the entire HashMap? 为什么在hashmap中使用Linkedlist?为什么不执行List的其他实现? - Why Linkedlist in hashmap?Why not other implementation of List? 为什么像 HashMap 或 LinkedList 这样的 Java 容器不重用在添加新元素时被删除的内部节点? - Why don`t Java containers like HashMap or LinkedList reuse internal Nodes which were deleted when adding new elements? 在 Java 的 Linkedlist 实现中,head 如何更新新节点 - How head is updating with new nodes in Linkedlist implementation in Java 为什么 ArrayDeque 比 LinkedList 好 - Why is ArrayDeque better than LinkedList 为什么HashSet的内部实现会创建虚拟对象以在HashMap中作为值插入而不是插入空值? - Why the internal implementation of HashSet creates dummy objects to insert as values in HashMap rather than inserting nulls? 仅使用叶节点将binaryTree转换为LinkedList - convert binaryTree to LinkedList with only Leaf Nodes 链表节点 - LinkedList Nodes
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM