android HashMap源码添加数据的全过程解析(四)

接上几节,我们分析了hashmap在put一个数据时,会发生的情况:

1、根据key,计算hash,确定下标;

2、当第一次put时,如果table为空,则会初始化table数组,长度为16;

2、当table内,无数据时,put的数据将直接tab[下标]=newNode();

3、当有数据时:

        3.1、当来源是同一个key时,则会将key设置成最后一次的put的value;

        3.2、当来源是一个新的时,则遍历节点,知道最后一个为null,这时会把node.next=newNode()进行赋值;

        3.3、当节点中,局部变量等于8-1时,则会将该链表进行treeifyBin()(暂时没讲)

4、增加数据后,判断++size > threshold,如果大于,则执行行resize()即动态扩容(暂时没讲)

那么接下来,就看看treeifyBin()和resize()方法;

    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

我们说,tab的size等于8-1的时候才执行,可以看出,这个tab是一个很长的链表;

    static final int MIN_TREEIFY_CAPACITY = 64;

可以看到,它会走到resize()方法。很奇怪,成员变量table的某个下标的长度大于等于8时,就进行resize()了,并不是我们说的达到table的0.75倍,它才resize();看看这个resize()究竟是什么;

    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        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; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            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);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            ...
        }
        return newTab;
    }

很长,我们省略了一部分;

下面我们看下条件继续走

int oldCap=16;
int oldThr = threshold=16;
int newCap, newThr = 0;

    static final int MAXIMUM_CAPACITY = 1 << 30;=1073741824


这里当然不会走: threshold = Integer.MAX_VALUE;


走到判断: else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold

 16 << 1 
=10000 << 1 (十进制16,它的二进制是10000)
=100000      (二进制100000,它的十进制是32)
=32
(大家看我写的这个等式,是不是有点眼熟,像不像小学的时候,做的加减乘除写的计算过程?)

newCap=32;它是小于1 << 30

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

并且它是大于16的,满足条件 
即:newThr = oldThr << 1;
   newThr = 16 << 1 
   newThr = 32

if (oldCap > 0) {}这里面的else if else if 都是为了处理新节点数组的长度即长度左移一位,从值上看,就是长度乘以2;

继续往下,我们看了新建note数组了:

        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;

现在table的长度就是32了,那原来的table上面的数据已经赋值给了oldTab,那数据都在oldTab,下面,它肯定是要将上面的数据,重新插入到新的table数据上了;

继续往下看,oldTab肯定不为空,则往下,来到for循环,肯定是节点遍历了:

            for (int j = 0; j < oldCap; ++j) {
                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 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;
                            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;
                        }
                    }
                }
            }

代码有很多;这里我们假设,原来老tab中 ,只有下标为11的,有1条链表,长度为8;

它这里通过e= oldTab[j],然后再oldTab[j] = null;

明天我们再分析;

猜你喜欢

转载自blog.csdn.net/jian11058/article/details/123073609
今日推荐