HashMap为什么要先插入再扩容JDK1.8

JDK1.8开始HashMap为什么要先插入后扩容,网上查找有说先扩容再插入可以少遍历之类的,其实不管是先扩容还是先插入,它的原则还是尾插法都是避免不了要遍历的,那它为什么还是要先插入呢,只要看插入逻辑和扩充逻辑做了哪些操作就知道了,以下也 只是个人的理解,如有错误欢迎指点

首先看下JDK1.8 HashMap插入的源码
1:插入操作如果数组中的节点是红黑树是往节点中插入节点, 如果是链表的时候可能会要从链表升级成红黑树,似乎先插入再扩容还是先扩容后插入都是没影响的都是要遍历, 那问题原因就在扩容机制里
else if (p instanceof TreeNode)
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
    for (int binCount = 0; ; ++binCount) {
        if ((e = p.next) == null) {
            p.next = newNode(hash, key, value, null);
            /**
            *  这里要满足当前链表的长度>=7,遍历是从根节点开始,因此相当于包括根节点有8个时再插入就会调用此方法
            */
            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                treeifyBin(tab, hash);
            break;
        }
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            break;
        p = e;
    }
}

2:再看看扩容的源码中有个关键的代码((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
if (loHead != null) {
    if (lc <= UNTREEIFY_THRESHOLD)
        tab[index] = loHead.untreeify(map);
    else {
        tab[index] = loHead;
        if (hiHead != null) // (else is already treeified)
            loHead.treeify(tab);
    }
}
if (hiHead != null) {
    if (hc <= UNTREEIFY_THRESHOLD)
        tab[index + bit] = hiHead.untreeify(map);
    else {
        tab[index + bit] = hiHead;
        if (loHead != null)
            hiHead.treeify(tab);
    }
}

扩容的时候如果红黑树节点的个数<=6个时,就会降级成链表了

HashMap为何将红黑树降级链表的阈值设置成6而不是7, 就是防止节点在红黑树与链表之间因插入扩容而频繁的转变,频繁的转变是要消耗性能,而插入时的操作中将红黑树变成链表的触发条件就是扩容
下面就举例频繁转变临界点的情况 ,根据频繁转变临界点的情况对比来说明原因,假设插入的节点刚好都在当前的链表或者红黑树的索引位置上
临界点情形一

假设当前插入节点是红黑树结构有7个, 如果先扩容后会有1个节点移到别的index索引时,导致当前红黑树的节点变成6个就会降级成链表再插入变成7个节点的链表。
而如果是先插入的话就是8个节点红黑树,然后扩容即使有1个节点移到别的位置也还是7个节点也不会转变成链表
\color{red}{而且即使大于7个节点的红黑树先扩容都有可能降成链表}
\color{red}{而先扩容再插入时如果连续再插入2个相同索引位置的节点链表就又会变红黑树}

临界点情形二

和情形一逻辑相反的情况,如果当前插入节点为8个且为链表的时候,如果先扩容后会有1个节点移到别的index索引时再插入还是8个节点链表不会变红黑树,而如果是先插入就会变成红黑树再扩容有1节点移动时变成8个节点的红黑树,如果此时移除节点又有可能降成链表,先分析下移除节点时降链表条件的源码

//这是移除时的部分源码
if (root == null || root.right == null ||  (rl = root.left) == null || rl.left == null) {
       tab[index] = first.untreeify(map);  // too small
       return;
}

可以看出在移除节点的时候不一定是6个节点就会导致红黑树降级成链表,根据红黑树的特点有可能当前链表 3 <= Nodes <=6时才会降级成链表
\color{red}{而且只有链表为8个节点的时候先插入才会变红黑树}
\color{red}{先插入再扩容时如果再连续移除最少2个最多5个节点的时候才又会降成链表}

\color{red}{结论:通过以上两种临界情形对比,很明显如果先扩容再插入时频繁转变的概率要比先插入后扩容频繁转变的概率高}

这里也给一个模拟HashMap造出红黑树的测试代码,红黑树为7个节点的时候,从64扩容到128时导致了key为64的节点移到第64的索引中去,而红黑树就变成了链表了
public static void main(String[] args) {
        //默认64个大小hashMap,只要数组长度>=64, 且链表 》=8时时才会转红黑树
        HashMap<Integer, String> map = new HashMap<>(64);
        map.put(0, "王二麻子 0");
        map.put(64, "王二麻子 1");
        map.put(128, "王二麻子 2");
        map.put(256, "王二麻子 3");
        map.put(512, "王二麻子 4");
        map.put(1024, "王二麻子 5");
        map.put(2048, "王二麻子 6");
        map.put(4096, "王二麻子 7");
        map.put(8192, "王二麻子 8");
        //以上步骤就会生成在第0的索引上的一个9个节点的红黑树结构
        map.remove(8192);
        map.remove(4096);
        //移除二个后此时是一个有7个节点的红黑树
        //这一步添加41个其他位置上的节点,只要不在0节点上就行,让节点总数达到 64*0.75 = 48个
        for (int i = 1; i <= 41; i++) {
            map.put(i, "张三 " + i);
        }
        //插入第49个时触发扩容,此后红黑树结构就变成链表结构了
        map.put(42, "李四");
    }
以上只是个人观点,如有错误欢迎指点
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容