[Java] HashMap部分源码阅读注释

113 阅读2分钟

putVal()源码注释

/**
     * HashMap putVal方法(put()调用)
     * @param hash
     * @param key
     * @param value
     * @param onlyIfAbsent
     * @param evict
     * @return
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;
        /*检查是否初始化table数组*/
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        /*如果hash对应的下标位置中没有元素(n-1)&hash与hash%n等价,
        (n-1)&hash使用的位运算,效率比模运算高*/
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {//下标位置已经有元素了
            HashMap.Node<K,V> e; K k;
            /*这个元素可能是就是本身,把p赋给e(p就是新插入进来的节点)*/
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            /*如果这个位置是红黑树存储了多个Node,就把p转换为TreeNode,然后插入红黑树*/
            else if (p instanceof HashMap.TreeNode)
                e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            /*拉链法解决hash冲突*/
            else {
                for (int binCount = 0; ; ++binCount) {
                    /*p指向的就是链表的第一个元素,e指向p的下一个,
                    为空意味着要把新插入的节点新增到拉链链表后面*/
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        /*TREEIFY_THRESHOLD默认值是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往下移*/
                    p = e;
                }
            }
            /*e指向的是新node(可能是旧key+新值,也可能是新key+新值)*/
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                /*onlyIfAbsent为ture表示不更改旧值*/
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                /*在LinkedHashMap中实现,在这里算是空方法*/
                afterNodeAccess(e);
                return oldValue;
            }
        }
        /**
         * modCount表示HashMap发生更改的次数,
         * 作用是fail-fast,会判断expectModCount和modCount是否相等,
         * 如果不相等意味着其他线程修改了HashMap,会报ConcurrentModificationException
         */
        ++modCount;
        /*size表示的是真实元素个数*/
        if (++size > threshold)
            resize();
        /*在这里是空方法,在LinkedHashMap中实现*/
        afterNodeInsertion(evict);
        /*插入的是新key+新value的时候会返回null*/
        return null;
    }

resize()源码注释

final HashMap.Node<K,V>[] resize() {
        HashMap.Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        /*第一次put不会进入这个判断,因为在构造方法中,table并没有初始化*/
        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; // *2
        }
        /*new HashMap(10)的情况,
        * 在构造方法中会threshhold = tableSizeFor(10) = 16*/
        else if (oldThr > 0)
            newCap = oldThr;
        /*new HashMap()的情况*/
        else {
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        /*new HashMap(10)&&第一次resize()才会进入
        * 目的是初始化map的threshold*/
        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"})
        HashMap.Node<K,V>[] newTab = (HashMap.Node<K,V>[])new HashMap.Node[newCap];
        table = newTab;
        /*第一次put不会进入这个判断
        * 目的是把旧table中的内容映射到新table
        * 只有两种情况,要么在原来的下标上,要么在2*原来下标上*/
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                HashMap.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 HashMap.TreeNode)
                        ((HashMap.TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        HashMap.Node<K,V> loHead = null, loTail = null;
                        HashMap.Node<K,V> hiHead = null, hiTail = null;
                        HashMap.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;
    }