HashMap 实现原理及源码解析(jdk8 底层⽤的是数组+链表/红⿊树)

导读:本篇文章讲解 HashMap 实现原理及源码解析(jdk8 底层⽤的是数组+链表/红⿊树),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

本文会通过HashMap中的put方法为入口,进行源码解读,文章较长,需要耐心阅读

说明

/** */: 代表注释,区别于例子注释
egx::代表例子注释

解读前须知

源码主要关注4⼤点:

  1. 确定哈希桶数组索引的位置
    HashMap.hash() ——> hashCode()
  2. 插⼊数据
    HashMap.put()
  3. 扩容机制
    resize()
  4. 红⿊树 ⾮常复杂,⼤家⾃学吧
    treeify()

解读是通过HashMap的put方法为入口进行分析,其中eg注释的都是测试例子,具体测试代码如下:

@Test
public void testResize(){
	HashMap hashMap = new HashMap();
	// 下标0和1插入Node
	hashMap.put(0, "a0");  // 00000000
	hashMap.put(1, "a1");  // 00000001
	
	// 下标0,横向插入多个Node形成链表
	hashMap.put(16, "a16");  // 00010000
	hashMap.put(32, "a32");  // 00100000
	hashMap.put(48, "a48");  // 00110000
	hashMap.put(64, "a64");  // 01000000
	hashMap.put(80, "a80");  // 01010000
	hashMap.put(96, "a96");  // 01100000
	hashMap.put(112, "a112");  // 01110000
	hashMap.put(128, "a128");  // 10000000
	// hashMap.put(144, "a144");  // 10010000
}

在这里插入图片描述

源码解读

解读之前,需要先了解下几个字段作用

	/**
     * 默认的初始容量
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * 最大容量,之后会看到很多次
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 默认的加载因子,构造函数可以指定
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 树化阈值,默认达到8个节点树化
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 被treeified的最小表容量,否则仅仅resize。
     * TREEIFY_THRESHOLD至少为4,以避免大小调整和treeification阈值之间的冲突。
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

先来看下put方法

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

put方法很简单,但注意,这里有一个hash的方法,进入hash方法

    static final int hash(Object key) {
        int h;
        /**
         * 按位异或运算(^):两个数转为二进制,然后从高位开始比较,如果相同则为0,不相同为1
         *
         * 扰动函数:(h = key.hashCode()) ^ (h >>> 16)
         *      将key的hashCOde一分为二
         *      【高半区16位】数据位不变
         *      【低半区16位】数据与高半区16位数据进行异或操作,以此来加大低位的随机性
         * 【注意】如果key的哈希code小于等于16位,那么是没有任何影响的。只有大于16位才会触发扰动函数的执行效果
         *
         * case1:
         *  h=高16位(全是0) and 低16位(有1)
         *  h >>> 16 = 低16位全部消失,那么变成了32位(全是0)
         *  h ^ (h >>> 16) = 原样输出
         * case2:
         *  h=高16位(有1) and 低16位(有1)
         *  h >>> 16 = 低16位全部消失,那么变成了高16位(全是0)and低16位(有1)
         *  h ^ (h >>> 16) = 不是原样输出  将原高16位于原低16位进行扰动。
         */
        // eg1: 110100100110^000000000000=110100100110,由于k1的hashCode都是在低16位,所以原样返回3366,没有扰动效果
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

这里其实就是利用高16位+低16位进行一个扰动,使得获取比较hash时,高16位也能参与hash,具体可以来看一张图比较明了
在这里插入图片描述
图中可以看到,h>16位的,则高16位与低16位进行异或,达到扰动效果,如果h<=16位的,则保持原样,没有扰动效果
put方法中除了hash,主要还是调用putVal进行实现,putVal方法如下

    /**
     * Implements Map.put and related methods.
     *
     * @param hash         key的hash值
     * @param key          key值
     * @param value        value值
     * @param onlyIfAbsent 如果是true,则不改变已存在的值
     * @param evict        如果为false,则表为创建模式
     * @return previous value, or null if none
     */
    // eg1: hash=0 key=0 value="a0" onlyIfAbsent=false evict=true
    // eg2: hash=1 key=1 value="a1" onlyIfAbsent=false evict=true
    // eg3: hash=16 key=16 value="a16" onlyIfAbsent=false evict=true
    // eg4: hash=32 key=32 value="a32" onlyIfAbsent=false evict=true
    // eg5: 由于执行步骤与eg4相似,故略过。
    // eg6: hash=128 key=128 value="a128" onlyIfAbsent=false evict=true
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K, V>[] tab;
        Node<K, V> p;
        int n, i;

        // eg1: table=null
        // eg2: table是长度为16的Node数组,且table[1]=Node(1, 1, "a1", null)
        // eg3: table是长度为16的Node数组,且table[1]=Node(1, 1, "a1", null) ... table[6]=Node(6, 6, "a6", null)
        // eg4: table是长度为16的Node数组,且table[1]=Node(1, 1, "a1", null) ... table[6]=Node(6, 6, "a6", null)
        // eg6: table是长度为16的Node数组,且table[1]=Node(1, 1, "a1", null) ... table[6]=Node(6, 6, "a6", null)
        /** 如果table是空的,则默认初始化一个长度为16的Node数组 */
        if ((tab = table) == null || (n = tab.length) == 0) {
            n = (tab = resize()).length;
        }

        // eg1: i = (n-1)&hash = (16-1)&0 = 1111&0000 = 0000 = 0; 即:p=tab[0]=null
        // eg2: i = (n-1)&hash = (16-1)&1 = 1111&0001 = 0001 = 1; 即:p=tab[1]=null
        // eg3: i = (n-1)&hash = (16-1)&16 = 1111&10000 = 0000 = 0; 即:p=tab[0]=Node(0, 0, "a0", null)
        // eg4: i = (n-1)&hash = (16-1)&32 = 1111&100000 = 0000 = 0; 即:p=tab[0]=Node(0, 0, "a0", null)
        // eg6: i = (n-1)&hash = (16-1)&128 = 1111&10000000 = 0000 = 0; 即:p=tab[0]=Node(0, 0, "a0", null)
        /** 如果计算后的i在tab中没有数据,则新增Node节点 */
        if ((p = tab[i = (n - 1) & hash]) == null) {
            // eg1: tab[0] = newNode(0, 0, "a0", null)
            // eg2: tab[1] = newNode(1, 1, "a1", null)
            tab[i] = newNode(hash, key, value, null);
        }
        /** 如果下标i计算后,在tab数组中已存在,则执行以下逻辑 */
        else {
            Node<K, V> e;
            K k;
            // eg3: p.hash==0, hash==16,所以返回false
            // eg4: p.hash==0, hash==32,所以返回false
            // eg6: p.hash==0, hash==128,所以返回false
            /** 如果与已存在的Node是相同的key值 */
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) {
                e = p;
            }
            // eg3: p instanceof Node,所以为false
            // eg4: p instanceof Node,所以为false
            // eg6: p instanceof Node,所以为false
            /** 如果已存在的Node是树节点 */
            else if (p instanceof TreeNode) {
                e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
            }
            /** 如果已存在的节点key不同且是普通节点,则循环遍历链式Node,并对比hash何key,如果都不相同,则将新的Node拼装到链表的末尾。如果相同则进行更新 */
            else {
                for (int binCount = 0; ; ++binCount) {
                    // eg3: p.next == null
                    // eg4-loop1: p.next == Node(16, 16, "a16", null) 不为空
                    // eg4-loop2: p.next == null
                    if ((e = p.next) == null) {
                        // eg3: p.next = newNode(16, 16, "a16", null);
                        // eg4-loop2: p.next == newNode(32, 32, "a32", null);
                        // eg6: p.next == newNode(128, 128, "a128", null);
                        p.next = newNode(hash, key, value, null);

                        // eg3: binCount == 0
                        // eg4-loop2: binCount == 1
                        /** 如果链表大于8个Node,那么变为红黑树 */
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        {
                            // eg6: tab={newNode(0, 0, "a0", [指向后面1个链表中的7个node]), newNode(1, 1, "a1", null)}, hash=128
                            /** 将链表变为红黑树 */
                            treeifyBin(tab, hash);
                        }
                        // eg3: break
                        // eg4-loop2: break
                        break;
                    }
                    // eg4-loop1: e.hash==16 hash==32 所以返回false
                    if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k)))) {
                        break;
                    }
                    // eg4-loop1: p=e=Node(16, 16, "a16", null)
                    p = e;
                }
            }

            // eg3: e = null
            // eg4: e = null
            /** 以上的多个判断e不等于null,则表示存在相同的key值 */
            if (e != null) { // existing mapping for key
                // egx: String oldValue = "v1"
                V oldValue = e.value;
                // egx: onlyIfAbsent=false
                if (!onlyIfAbsent || oldValue == null) {
                    // egx: e = Node(3366, "k1", "v2", null)
                    /** 则将Node(e)新的value值进行更新 */
                    e.value = value;
                }
                afterNodeAccess(e);
                // egx: 返回oldValue="v1"
                return oldValue;
            }
        }

        // eg1: modCount==0 ++modCount==1
        // eg2: modCount==1 ++modCount==2
        // eg3: modCount==7 ++modCount==8
        // eg4: modCount==8 ++modCount==9
        ++modCount;

        // eg1: size=0, threshold=12
        // eg2: size=1, threshold=12
        // eg3: size=7, threshold=12
        // eg4: size=8, threshold=12
        /** size+1>threshold,说明需要扩容了 */
        if (++size > threshold) {
            resize();
        }
        afterNodeInsertion(evict);
        return null;
    }

这个方法包含了主要的逻辑,具体功能可以详细看注释,其中有两个地方执行了resize()方法,一个是table为空的时候;一个是size达到了阈值,需要进行扩容,我们来看看reseze()方法

    /**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     * <p>
     * 【重要】table扩容
     * <p>
     * 常量名词解释
     * Tab->Table 表
     * Cap->Capacity 容量
     * Thr->Threshold 阈值
     */
    final Node<K, V>[] resize() {
        // eg1: table=null
        // eg6: table != null
        Node<K, V>[] oldTab = table;
        // eg1: oldCap=0
        // eg6: oldCap=16
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        // eg1: oldThr=threshold=0
        // eg6: oldThr=threshold=12
        int oldThr = threshold;
        int newCap, newThr = 0;
        /** 第一步:根据情况调整新表的容量newCap和阈值newThr */
        if (oldCap > 0) {
            /** 如果老的table的长度大于等于2^30(1 << 30) */
            if (oldCap >= MAXIMUM_CAPACITY) {
                /** threshold=2^31-1 */
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            /** 如果将老的容量oldCap*2做为新的容量newCap后还是小于2^30并且老的容量大于16(1<<4) */
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) {
                // eg6: newCap=32, newThr=24
                /** newThr=oldThr*2 */
                newThr = oldThr << 1;
            }
        }
        // 初始容量设为阈值
        else if (oldThr > 0) {
            newCap = oldThr;
        } else {               // zero initial threshold signifies using defaults
            // eg1: oldCap=0 newCap=16 newThr=0.75f*16=12
            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);
        }

        // eg1: threshold=newThr=12
        // eg6: threshold=newThr=24
        threshold = newThr;

        /** 第二步:根据newCap和newThr,构建新数组(初始化新表) */
        @SuppressWarnings({"rawtypes", "unchecked"})
        Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
        // eg1: table=newTab=(Node<K, V>[]) new Node[16];
        // eg6: table=newTab=(Node<K, V>[]) new Node[32];
        table = newTab;
        // eg1: oldTab=null
        /** 如果老的oldTab里面有数据,则进行数据迁移到新的newTab */
        if (oldTab != null) {
            // eg6: oldCap=16
            for (int j = 0; j < oldCap; ++j) {
                Node<K, V> e;
                // eg6-loop1: j=0, e=oldTab[0]=Node(0, 0, "a0", nodeRef)
                // eg6-loop2: j=1, e=oldTab[1]=Node(1, 1, "a1", null)
                if ((e = oldTab[j]) != null) {
                    // eg6-loop1: oldTab[0] = null
                    // eg6-loop2: oldTab[1] = null
                    /** 将j位置的Node置空 */
                    oldTab[j] = null;
                    // eg6-loop1: e.next=Node(16, 16, "a16", nodeRef)
                    // eg6-loop2: e.next==null
                    /** 如果没有后置节点,说明e是最后一个节点 */
                    if (e.next == null) {
                        // eg6-loop2: e.hash==1, newCap=32, 1&(32-1)==1 即:newTab[1]=Node(1, 1, "a1", null)
                        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 {
                            // eg6-loop1-loop1: next=e.next=Node(16, 16, "a16", nodeRef)
                            // eg6-loop1-loop2: next=e.next=Node(32, 32, "a32", nodeRef)
                            // eg6-loop1-loop3: next=e.next=Node(48, 48, "a48", nodeRef)
                            // eg6-loop1-loop4: next=e.next=Node(64, 64, "a64", nodeRef)
                            // eg6-loop1-loop5: next=e.next=Node(80, 80, "a80", nodeRef)
                            // eg6-loop1-loop6: next=e.next=Node(96, 96, "a96", nodeRef)
                            // eg6-loop1-loop7: next=e.next=Node(112, 112, "a112", nodeRef)
                            // eg6-loop1-loop8: next=e.next=Node(128, 128, "a128", nodeRef)
                            // eg6-loop1-loop9: next=e.next=null
                            /** 获取oldTable数组下标的下一个节点 */
                            next = e.next;

                            // eg6-loop1-loop1: e.hash=0, oldCap=16,  00000000&10000==00000==0
                            // eg6-loop1-loop2: e.hash=16, oldCap=16, 00010000&10000==10000==16
                            // eg6-loop1-loop3: e.hash=32, oldCap=16, 00100000&10000==00000==0
                            // eg6-loop1-loop4: e.hash=48, oldCap=16, 00110000&10000==10000==16
                            // eg6-loop1-loop5: e.hash=64, oldCap=16, 01000000&10000==00000==0
                            // eg6-loop1-loop6: e.hash=80, oldCap=16, 01010000&10000==00000==16
                            // eg6-loop1-loop7: e.hash=96, oldCap=16, 01100000&10000==00000==0
                            // eg6-loop1-loop8: e.hash=112, oldCap=16, 01110000&10000==10000==16
                            // eg6-loop1-loop9: e.hash=128, oldCap=16, 10000000&10000==10000==0
                            /** 计算e在老表oldTable的下标,如果是第一个Node,即下标index==0 */
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null) {
                                    // eg6-loop1-loop1: loHead=e=Node(0, 0, "a0", nodeRef)
                                    /** 将loHead指向oldTable数组的第一个元素e */
                                    loHead = e;
                                } else {
                                    // eg6-loop1-loop3: loTail.next=e=Node(32, 32, "a32", nodeRef)
                                    // eg6-loop1-loop5: loTail.next=e=Node(64, 64, "a64", nodeRef)
                                    // eg6-loop1-loop7: loTail.next=e=Node(96, 96, "a96", nodeRef)
                                    // eg6-loop1-loop9: loTail.next=e=Node(128, 128, "a128", nodeRef)
                                    loTail.next = e;
                                }
                                // eg6-loop1-loop1: loTail=e=Node(0, 0, "a0", nodeRef)
                                // eg6-loop1-loop3: loTail=e=Node(32, 32, "a32", nodeRef)
                                // eg6-loop1-loop5: loTail=e=Node(64, 64, "a64", nodeRef)
                                // eg6-loop1-loop7: loTail=e=Node(96, 96, "a96", nodeRef)
                                // eg6-loop1-loop9: loTail=e=Node(128, 128, "a128", nodeRef)
                                /** 将loTail指向oldTable中的第一个元素e */
                                loTail = e;
                            } else {
                                if (hiTail == null) {
                                    // eg6-loop1-loop2: hiHead=e=Node(16, 16, "a16", nodeRef)
                                    hiHead = e;
                                } else {
                                    // eg6-loop1-loop4: hiTail.next=e=Node(48, 48, "a48", nodeRef)
                                    // eg6-loop1-loop6: hiTail.next=e=Node(80, 80, "a80", nodeRef)
                                    // eg6-loop1-loop8: hiTail.next=e=Node(112, 112, "a112", nodeRef)
                                    hiTail.next = e;
                                }
                                // eg6-loop1-loop2: hiTail=e=Node(16, 16, "a16", nodeRef)
                                // eg6-loop1-loop4: hiTail=e=Node(48, 48, "a48", nodeRef)
                                // eg6-loop1-loop6: hiTail=e=Node(80, 80, "a80", nodeRef)
                                // eg6-loop1-loop8: hiTail=e=Node(112, 112, "a112", nodeRef)
                                hiTail = e;
                            }

                            // eg6-loop1-loop1: e=next=Node(16, 16, "a16", nodeRef)
                            // eg6-loop1-loop2: e=next=Node(32, 32, "a32", nodeRef)
                            // eg6-loop1-loop3: e=next=Node(48, 48, "a48", nodeRef)
                            // eg6-loop1-loop4: e=next=Node(64, 64, "a64", nodeRef)
                            // eg6-loop1-loop5: e=next=Node(80, 80, "a80", nodeRef)
                            // eg6-loop1-loop6: e=next=Node(96, 96, "a96", nodeRef)
                            // eg6-loop1-loop7: e=next=Node(112, 112, "a112", nodeRef)
                            // eg6-loop1-loop8: e=next=Node(128, 128, "a128", nodeRef)
                            // eg6-loop1-loop9: e=next=null
                        } while ((e = next) != null);
                        // eg6-loop1: loTail=Node(128, 128, "a128", null)
                        if (loTail != null) {
                            loTail.next = null;
                            // eg6-loop1: j=0, newTab[0]=loHead=Node(0, 0, "a0", nodeRef)
                            newTab[j] = loHead;
                        }

                        // eg6-loop1: loTail=Node(112, 112, "a112", nodeRef)
                        if (hiTail != null) {
                            // eg6-loop1: loTail=Node(112, 112, "a112", null)
                            hiTail.next = null;
                            // eg6-loop1: j=0, oldCap=16, newTab[16]=hiHead=Node(16, 16, "a16", nodeRef)
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        // eg1: newTab = (Node<K, V>[]) new Node[16]
        // eg6: newTab = (Node<K, V>[]) new Node[32]
        return newTab;
    }

这里主要有如下几种情况:

  1. 老的容量大于0,且老的容量大于2^30 ,则设置新的阈值为,2^31-1
  2. 老的容量大于0,且老的容量2大于2^30且老的容量大于默认初始化容量16,则新的阈值为老的容量2
  3. 老的阈值大于0,新容量=老的阈值大小
  4. 其它情况,新容量等于默认初始化容量16,新阈值等于默认的加载因子(DEFAULT_LOAD_FACTOR)*默认初始化容量(DEFAULT_INITIAL_CAPACITY)

接下来就是对确定好的阈值和容量进行构建新表,遍历oldTab的时候,执行逻辑有如下几种情况:

  1. 如果是叶子节点,直接将e赋值给newTab
  2. 如果是树节点(TreeNode),调用split方法
  3. 其它情况,构建链表,并将表头赋值给newTab

这里有一个关键方法,就是split,这个方法是在TreeNode私有类中,方法主要作用是将tree bins中的节点分成上下两个tree bins,如果tab太小就取消split,仅调整大小,我们来详细看下:

/**
         * 将tree bins中的节点分成上下两个tree bins,如果tab太小就取消split,仅调整大小
         *
         * @param map   the map
         * @param tab   the table for recording bin heads
         * @param index the index of the table being split
         * @param bit   the bit of hash to split on
         */
        final void split(HashMap<K, V> map, Node<K, V>[] tab, int index, int bit) {
            // 做个赋值,因为这里是((TreeNode<K,V>)e)这个对象调用split()方法,所以this就是指(TreeNode<K,V>)e对象,所以才能类型对应赋值
            TreeNode<K, V> b = this;
            // 设置低位首节点和低位尾节点
            TreeNode<K, V> loHead = null, loTail = null;
            // 设置高位首节点和高位尾节点
            TreeNode<K, V> hiHead = null, hiTail = null;
            // 定义两个变量lc和hc,初始值为0,后面比较要用,他们的大小决定了红黑树是否要转回链表
            int lc = 0, hc = 0;
            // 这个for循环就是对从e节点开始对整个红黑树做遍历
            for (TreeNode<K, V> e = b, next; e != null; e = next) {
                // 取e的下一节点赋值给next遍历
                next = (TreeNode<K, V>) e.next;
                // 取好e的下一节点后,把它赋值为空,方便GC回收
                e.next = null;
                // 以下的操作就是做个按位与运算,按照结果拉出两条链表
                if ((e.hash & bit) == 0) {
                    if ((e.prev = loTail) == null) {
                        loHead = e;
                    } else {
                        loTail.next = e;
                    }
                    loTail = e;
                    // 做个计数,看下拉出低位链表下会有几个元素
                    ++lc;
                } else {
                    if ((e.prev = hiTail) == null) {
                        hiHead = e;
                    } else {
                        hiTail.next = e;
                    }
                    hiTail = e;
                    // 做个计数,看下拉出高位链表下会有几个元素
                    ++hc;
                }
            }

            // 如果低位链表首节点不为null,说明有这个链表存在
            if (loHead != null) {
                // 如果链表下的元素小于等于6
                if (lc <= UNTREEIFY_THRESHOLD) {
                    // 那就从红黑树转链表了,低位链表,迁移到新数组中下标不变,还是等于原数组到下标
                    tab[index] = loHead.untreeify(map);
                } else {
                    // 低位链表,迁移到新数组中下标不变,还是等于原数组到下标,把低位链表整个拉到这个下标下,做个赋值
                    tab[index] = loHead;
                    // 如果高位首节点不为空,说明原来的红黑树已经被拆分成两个链表了
                    if (hiHead != null) {
                        //那么就需要构建新的红黑树了
                        loHead.treeify(tab);
                    }
                }
            }
            // 如果高位链表首节点不为null,说明有这个链表存在
            if (hiHead != null) {
                // 如果链表下的元素小于等于6
                if (hc <= UNTREEIFY_THRESHOLD) {
                    // 那就从红黑树转链表了,高位链表,迁移到新数组中的下标=【旧数组+旧数组长度】
                    tab[index + bit] = hiHead.untreeify(map);
                } else {
                    // 高位链表,迁移到新数组中的下标=【旧数组+旧数组长度】,把高位链表整个拉到这个新下标下,做赋值
                    tab[index + bit] = hiHead;
                    // 如果低位首节点不为空,说明原来的红黑树已经被拆分成两个链表了
                    if (loHead != null) {
                        // 那么就需要构建新的红黑树了
                        hiHead.treeify(tab);
                    }
                }
            }
        }

就是将一个链表拆分为两个链表,完成拆分后判断每个链表的元素个数,情况如下:

  1. 个数小于等于6(UNTREEIFY_THRESHOLD),则将红黑树转链表
  2. 个数大于6,且头节点不为空,则将链表转红黑树

为了便于大家理解,我们通过一张图来理解以上的split的方法:
在这里插入图片描述

这个方法中有涉及到红黑树转换,我们来了解下treeifyBin方法:

在之前putVal方法中,也有进行转红黑树的操作,调用方法为treeifyBin,和treeify的区别主要是增加了条件判断,比如表长度是否满足最小数容量的要求,如果不是仅仅做扩容操作resize,我们来看看具体实现:

/**
     * 依据给定的hash替换此节点为树形节点,并树形化。如果表太小则是调整容量resize。
     */
    // eg6: hash=128
    // tab[0]=Node(0, 0, "a0", nodeRef)——>Node(16, 16, "a16", nodeRef)——>Node(32, 32, "a32", nodeRef)——>Node(48, 48, "a48",nodeRef)——>Node(64, 64, "a64", node)——>Node(80, 80, "a80", node)——>Node(96, 96, "a96", node)——>Node(112, 112, "a112", node)
    // tab[1]=Node(1, 1, "a1", null)
    final void treeifyBin(Node<K, V>[] tab, int hash) {
        int n, index;
        Node<K, V> e;
        //  eg6: tab !=null, tab.length=16
        /** 当tab的长度小于64,则不转换为数,只扩展数组大小 */
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) {
            //  eg6: 执行resize()
            resize();

            /** 如果新增的node要插入的数组位置已经有node存在,则取出该位置的node为e */
        } else if ((e = tab[index = (n - 1) & hash]) != null) {
            /** 头节点 */
            TreeNode<K, V> hd = null;
            /** 尾节点 */
            TreeNode<K, V> tl = null;
            do {
                /** 将Node转换为TreeNode->new TreeNode<>(p.hash, p.key, p.value, next); */
                TreeNode<K, V> p = replacementTreeNode(e, null);
                /** 将每个Node转换尾TreeNode,并且用prev和next指针进行链接,并以hd为头节点 */
                if (tl == null) {
                    hd = p;
                } else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);

            /** 如果为头节点的hd不为空,则进行数形化操作 */
            if ((tab[index] = hd) != null) {
                hd.treeify(tab);
            }
        }
    }

满足树形化条件则调用了treeify方法,至此所有的执行操作基本了解了

总结

HashMap容器逻辑还是比较复杂的,大家可以针对注释和eg例子,再配合流程图,就能够很好的理解HashMap的put过程,至于其它逻辑比较简单,就没有详细说明
使用流程图来看看整体的put操作:
在这里插入图片描述

如果喜欢我的文章记得一定要一键三连哟(别下次一定!!!)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/17880.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!