您的位置:首页 > 理论基础 > 数据结构算法

共同学习Java源代码-数据结构-HashMap(七)

2017-11-07 17:01 218 查看
    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;
    }

首先将原来的哈希桶数组赋引用给临时变量oldTab 然后将原有哈希桶的长度赋给临时变量oldCap 然后将原有扩容门槛变量值赋给临时变量oldThr

创建临时变量newCap newThr 代表新的哈希桶数组长度和新的扩容门槛

判断旧的容量是否大于零 如果大于零 就继续判断 如果旧的容量大于等于二的三十次方 那么将旧的扩容门槛设为int类型的最大值 直接返回旧的哈希桶数组 如果旧哈希桶的容量小于最大容量 且最大容量乘以2仍然小于最大容量 且旧容量大约等于默认初始容量16 将旧容量乘以二赋给新容量 将旧的扩容门槛乘以二赋给新的扩容门槛

如果旧容量小于等于0 也就是空的哈希桶 但旧的扩容门槛大于0 将新的容量赋值为旧的扩容门槛 

如果旧容量小于等于0 且旧的扩容门槛小于等于0 那么 新的容量就是默认值16 新的扩容门槛就是16乘以0.75 

然后再判断新的扩容门槛是否等于0 新的扩容门槛等于0的情况一般是旧的容量大于0但小于16 或者旧的容量等于0但旧的扩容门槛大于0 一旦新的扩容门槛为0 那么判断如果新的容量小于最大容量且新的容量乘以载荷的乘积小于最大容量的话 将新的容量乘以载荷的乘积作为新的扩容门槛 否则就是将int类型最大值作为扩容门槛

然后将新的扩容门槛赋值给threshold

然后创建一个新的节点数组 长度为新容量 并赋给table

判断如果旧的哈希桶数组不为空 就遍历旧的哈希桶数组 将数据转移到新的哈希桶数组里

遍历旧哈希桶数组 每次遍历创建一个节点对象e 

将旧哈希桶的每个元素 也就是每个链表的第一个元素或每棵树的根元素赋给e 如果这棵树的根元素或这个链表的第一个元素不为空 也就是原先这个桶有数据 先将旧的桶清空  再判断如果这个根元素或第一个元素没有下一个节点 也就是说这个桶就一个节点 新的哈希桶数组的第e.hash & (newCap - 1)个元素就引用旧哈希桶中这个元素 判断如果这个桶的链表已经变成树 就调用树的split方法转移数据 如果旧哈希桶对应元素是链表 且还有下一节点 就创建两条链表头节点和尾节点分别为loHead hiHead loTail
hiTail 再创建一个next节点

进入do while循环 循环体为判断该元素的hash值和旧容量的按位与值 如果为0 就插入loTail loHead这个链表 否则就插入hiHead hiTail这个链表 插入链表的步骤完全一样 就是判断链表尾部是否为空 为空的话首元素就是这个元素 不为空的话 尾元素的下一个元素就是这个元素 然后将这个元素设置为新的尾元素 循环体就这样 循环条件就是旧的哈希桶对应链表仍可以遍历 

跳出循环后 判断刚才操作的是哪个链表 将这两条链表的尾部的下一个元素设为空 将新哈希桶中同一位置引用为lo系列联表的首元素 将新哈希桶中同一位置+旧哈希桶长度偏移量的位置引用hi系列链表的首元素 我猜这么做是为了保证扩容后新的链表们 能平均分布在新的哈希桶里 就是将原来的桶对应链表拆成两个链表 放在新哈希桶里的不同位置
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: