前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HashMap扩容机制

HashMap扩容机制

作者头像
用户6055494
发布2019-10-15 23:10:19
9390
发布2019-10-15 23:10:19
举报
文章被收录于专栏:AVAJ

HashMap的扩容机制 上一期已经讲到了添加元素的put方法了,现在回顾一下put方法,主要讲解扩容方法:

调用的是putVal方法:

经过插入元素插入之后,改变桶的大小,判断是否超过阀值(阀值 = 加载因子 * 桶容量),超过就扩容:

代码语言:javascript
复制
final Node<K,V>[] resize() {    Node<K,V>[] oldTab = table;    // oldTable 为null的话说明这个HashMap还没被初始化过    int oldCap = (oldTab == null) ? 0 : oldTab.length;    // 用来判断是否扩容的阀值,如果没被初始化过的HashMap这个oldCap是为0的    int oldThr = threshold;    int newCap, newThr = 0;    // oldCap > 0说明是已经使用过的HashMap现在添加元素导致产生扩容    if (oldCap > 0) {        // 旧容量如果大于 MAXIMUM_CAPACITY(1 << 30)        if (oldCap >= MAXIMUM_CAPACITY) {            // 将阀值赋值为int类型所能标识的最大值            // 由于有阀值存在 使得HashMap提前扩容了            // threshold = Integer.MAX_VALUE 的意思是充分使用空间            threshold = Integer.MAX_VALUE;            return oldTab;        }        // 如果容量左移1位也就是扩大2倍要比MAXIMUM_CAPACITY(1 << 30)小        // 并且要比初始容量DEFAULT_INITIAL_CAPACITY(1 << 4 = 16)要大        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&                 oldCap >= DEFAULT_INITIAL_CAPACITY)            // 阀值也进行扩容 并且容量是之前的2倍                 newThr = oldThr << 1; // double threshold    }    else if (oldThr > 0) // initial capacity was placed in threshold      // 如果旧的阀值要大于0 也就是在调用非默认的HashMap构造方法时      // 第一次插入数据才会到这儿        newCap = oldThr;    else {               // zero initial threshold signifies using defaults        // 否则的话就是用默认的大小 计算出阀值进行复制        newCap = DEFAULT_INITIAL_CAPACITY;        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);    }    if (newThr == 0) {        // 如果阀值为0那么就求出阀值然后赋值,小伙伴肯定有疑问:何时阀值才会为0呢?        // 如果调用非默认构造方法第一次put操作就会到这里        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) {        // 老的桶不为null的话 就转移元素        // 循环遍历        for (int j = 0; j < oldCap; ++j) {            Node<K,V> e;            // 在oldTab[j]上存在元素 赋值给e            if ((e = oldTab[j]) != null) {                // help GC                 oldTab[j] = null;                // e元素没有后续节点                if (e.next == null)                    // 直接计算e在扩容后的新桶上的位置 然后放入 注意:能进到这里每次是第一次                    // 访问oldTab中的每个位置的元素,在新桶里定位是不可能存在元素的 所以直接插入                    newTab[e.hash & (newCap - 1)] = e;                    // 如果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;                        // 在链表上的元素和老的容量的与操作只会有俩种情况                        // 等于0和不等于0 这样就分成俩个链表了 等于0的还在老位置上                        // 这里通过loHead loTail  hiHead hiTail 解决了1.7版本及之前的并发死循环问题                        // 详情可以看我写的另一篇关于HashMap死循环问题的文章                        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;                        // 扩容是原来的俩倍 所以与老容量与操作不为0的元素要搬移,而这个位置                        // 正好是当前位置+老的容量                        newTab[j + oldCap] = hiHead;                    }                }            }        }    }    return newTab;}

以上就是HashMap的扩容机制啦,感谢您宝贵的时间!

今天你只能好看

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员面试鸭 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档