HashMap源码分析(二)

160 阅读2分钟

这一节具体分析下put的主要源码,下面先贴上源码再一步一步分析。

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int number, index;
        if ((tab = table) == null || (number = tab.length) == 0)
            number = (tab = resize()).length;
        if ((p = tab[index = (number - 1) & hash]) == null)
            tab[index] = newNode(hash, key, value, null);
        else {
            Node<K,V> elememt; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                elememt = p;
            else if (p instanceof TreeNode)
                elememt = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((elememt = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (elememt.hash == hash &&
                        ((k = elememt.key) == key || (key != null && key.equals(k))))
                        break;
                    p = elememt;
                }
            }
            if (elememt != null) { // existing mapping for key
                V oldValue = elememt.value;
                if (!onlyIfAbsent || oldValue == null)
                    elememt.value = value;
                afterNodeAccess(elememt);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

put的具体细节

put简单说就是这个值如果不在里面就直接放进去,如果已经有了就替换,如果满了就扩容后再放进去。

  • 首先第一个if作用是为了判空,如果为true就去调用resize这个方法,这个方法不光是可以用来扩容,也做初始化的操作,如果时null或空的时候会根据设置的初始化容量来设置初始化。
  • 第二个if先给index赋值然后判断tab数组在这个位置上是不是null,true就说明这个位置还没被使用就直接给这个位置通过newnode方法赋值
  • else中的第一个if目的是为了判断要存放的map和当前的map是不是同一个,通过对比hash值和key,都相同就证明这两个是同一个,那就直接把这个p赋值给element
  • 第二个if判断当前节点是不是树节点,如果是就用树的增加方式来增加并把返回的treenode给element
  • 最后else是一个无限for循环
    • 首先判断element.next指向的是不是null,true就直接newnode然后让p.next指向这个新建的node,这一步用的是尾插法,不断往链表后面去插入新的节点(java8之前用的是头插法和这个有点区别),紧接着判断当前记录的bincount数是不是已经超过了树的阈值,超过了就转化成红黑树(在java8之后才会转化成红黑树),然后跳出循环
    • 如果p.next不是null而是已经有值那就判断和要存放的是不是相同的node,判断hash和key,相同就直接跳出循环。
    • 如果以上都不是那就把element给p
  • 如果element不是null,判断element.value是null或者onlyIfAbsent(允许改变已经存在的值),就把element的value改成要保存的value。afterNodeAccess这个方法是给linkedhashmap使用的
  • 最后就是增加modecount(修改次数),并且判断当前size是不是已经超过了阈值,如果超过了就调用resize来扩容。