探索HashMap

本文详细剖析了HashMap的内部工作原理,包括初始化、put操作、哈希计算以及resize过程。讲解了负载因子、阈值、容量等关键参数的作用,特别是resize时如何根据旧容量和新容量进行数据迁移,以及链表到红黑树的转换条件。内容涵盖HashMap的存储结构、哈希冲突解决和扩容策略,对于理解HashMap的高效性能有重要帮助。

·HashMap相关参数:

局部变量:

        存储数据的Node数组 table

        阈值 Threshold

        容量capacity

全局变量:

        加载因子 final float loadFactor

        阈值 int threshold

        修改次数 int modCount

        kv映射数量 transient int size

        数据存储 transient Node<K,V>[] table

·new HashMap<>();

给负载因子赋初始值为0.75

/**
 * 给负载因子赋初始值为0.75
 */
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
static final float DEFAULT_LOAD_FACTOR = 0.75f;

·put(K key, V value)

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

 hash运算:运算结果为:高16位不变,低16位为高16位与低16位进行异或运算(也叫扰动函数),所以如果高16位全0,则将原值原样输出,否则将异或运算后的结果输出。

public native int hashCode();

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

key.hashCode():根据key的不同类型,在对应的类中有对应的实现,例如:key为int类型:

public static int hashCode(int value) {
    return value;
}

String的hashCode():

public int hashCode() {
    // 初始化 hash=0 h=0
    int h = hash;
    // value={'k','1'} value.length=2
    // 只有第一次计算hash值时,才进入下面逻辑中。此后调用hashCode方法,都直接返回hash
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            // val[0]=107 val[1]=49
            h = 31 * h + val[i];
        }
        // h=31(31*0+107)+49=3366
        hash = h;
    }
    return h;
}

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) {
        // eg1: resize返回(Node<K, V>[]) new Node[16],所以:tab=(Node<K, V>[]) new Node[16], n=16
        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);
    } else { // 如果计算后的下标i,在tab数组中已存在数据,则执行以下逻辑
        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
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) { // 如果与已存在的Node是相同的key值
            e = p;
        }
        // eg3: p instanceof Node,所以为false
        // eg4: p instanceof Node,所以为false
        // eg6: p instanceof Node,所以为false
        else if (p instanceof TreeNode) { // 如果与已存在的Node是相同的key值,并且是树节点
            e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
        } else { // 如果与已存在的Node是相同的key值,并且是普通节点,则循环遍历链式Node,并对比hash和key,如果都不相同,则将新的Node拼装到链表的末尾。如果相同,则进行更新。
            for (int binCount = 0; ; ++binCount) {
                // eg3: p.next == null
                // eg4-loop1: p.next == Node(16, 16, "a16", null) 不为空
                // eg4-loop2: p.next == null
                // 获得p节点的后置节点,赋值给e。直到遍历到横向链表的最后一个节点,即:该节点的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
                    // binCount从0开始,如果Node链表大于8个Node,那么试图变为红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1) {
                        // 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
                // 针对链表中的每个节点,都来判断一下,是否待插入的key与已存在的链表节点相同,如果相同,则跳出循环,并在后续的操作中,将该节点内容更新为最新的插入值
                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
        // 如果存在相同的key值
        if (e != null) {
            // egx: String oldValue = "v1"
            V oldValue = e.value;
            // egx: onlyIfAbsent=false
            if (!onlyIfAbsent || oldValue == null) {
                // egx: e = Node(3366, "k1", "v2", null)
                // 则将新的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
    if (++size > threshold) {
        resize();
    }
    afterNodeInsertion(evict); // doing nothing
    return null;
}

resize的条件:

1.数组为空第一次put的时候

2.当map中的元素超过阈值的时候

3. 在jdk1.8中,当某一数组的链表长度大于等于8时,尝试进行红黑树的转换,如果数组的长度小于64,此时进行resize而不是进行红黑树的转换;只有数组的长度大于等于64时,才转换成红黑树。

resize的流程图:

第一阶段:根据旧的capacity、threshold和table计算出新的capacity、threshold,第一次扩容,容量扩为16,后面扩容按照2倍扩容,如果old capacity已经大于等于integer的max_Value,将threshold设置为integer的Max_Value,返回原表

 接上图:

第二阶段:根据新的capacity和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 = 0;
    int newThr = 0;
    // 第一步:根据情况,调整新表的容量newCap和阈值newThr
    if (oldCap > 0) {
        // 如果老table的长度大于等于2^30(1 << 30)        1后面有30个0
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE; // 2^31-1  1后面有30个1
            return oldTab;
        }
        // 如果将老Table的长度增长2倍作为新的容量长度(newCap),是否小于2^30(1 << 30) 并且 老Table长度是否大于等于16(1 << 4)
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) {
            // eg6: newCap=32, newThr=24
            newThr = oldThr << 1;
        }
    } else if (oldThr > 0) {
        newCap = oldThr;
    } else {
        // eg1: oldCap=0 newCap=16 newThr=0.75f*16=12
        newCap = DEFAULT_INITIAL_CAPACITY; // 默认【表容量】为16(1 << 4)
        newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 默认【阈值因子】为0.75f
    }
    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
    if (oldTab != null) { // 如果老的table里有数据,则进行数据迁移
        // eg6: oldCap=16
        // 循环纵向数组中的每个槽位Cap 
        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
                oldTab[j] = null;
                // eg6-loop1: e.next=Node(16, 16, "a16", nodeRef)
                // eg6-loop2: e.next==null
                if (e.next == null) { // 没有后置节点,说明e是最后一个节点
                    // eg6-loop2: e.hash==1, newCap=32, 1&(32-1)==1 即:newTab[1]=Node(1, 1, "a1", null)
                    newTab[e.hash & (newCap - 1)] = e;
                } else if (e instanceof TreeNode) { // e是树节点
                    ((TreeNode<K, V>) e).split(this, newTab, j, oldCap);
                } else {
                    Node<K, V> loHead = null;
                    Node<K, V> loTail = null;
                    Node<K, V> hiHead = null;
                    Node<K, V> 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
                        next = e.next; // 获得oldTab数组下标的Node列表的下一个节点
                        // 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
                        if ((e.hash & oldCap) == 0) { // 计算e在老表oldTab的下标,如果是第一个Node,即:下标index==0
                            if (loTail == null) {
                                // eg6-loop1-loop1: loHead=e=Node(0, 0, "a0", nodeRef)
                                loHead = e; // 将loHead指向oldTab数组第一个下标的第一个元素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 = e; // 将loTail指向oldTab数组第一个下标的最后一个元素e
                        } else { // 如果不是oldTab中的第一个下标Node
                            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); // do-while
                    // 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;
}

注:进行resize和put时,会进行当前元素的位置的计算,比如第一次插入时,初始化容量为16,key为0,此时hash之后的值也是0,根据槽位的计算公式:hash(key)&(capacity-1)   (-1是为了保证低位对&运算无影响计算得槽位为0,key为16、32时计算结果与0相同,都在0位,所以直接接在Node0之后(jdk1.8及之后是尾插法,所以接在后面),而当扩容的时候,此时的capacity为32,再次根据公式进行计算,得到的结果是0-0,16-16,32-0,这就表示,Node0放在0号位,Node16放到16号位,Node32放到0号位,接在Node0之后。

·get(Object key)

public V get(Object key) {
    Node<K, V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值