HashMap put方法 学习理解

put

// 新增元素
public V put(K key, V value) {
     
		// hash(key)  value  onlyIfAbsent(只新增不替换)   第四个参数暂时没用	
        return putVal(hash(key), key, value, false, true);
}

hash

// 求hash 
static final int hash(Object key) {
     
		// 定义局部变量
        int h;
        // 1. 如果key == null 返回 0  也就是说null 会坐落再0下表这个位置
        // 2. 求hash 再异或一个 h>>> 16 这是为啥呢 
        // 2.1 异或:俩值不同 结果为1 
        // 2.2 int是四个字节 4*8=32 如果直接&长度(长度没那么大一般) 那么高16位可能被丢失 增大碰撞的概率
        // 2.3 其实是为了减少碰撞 降低hash冲突的几率 
        // 2.4 为什么能减少碰撞呢  高16位异或低16位 高低位的数据特征都会被保留下来 
        // 这样就算高16位被丢失 那么hash的随机也不会被破坏 
        // 2.5 不用& 和 | 是因为他俩一个趋近1  一个趋近0  不随机 
	    
	  
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

putVal

/**
     * Implements Map.put and related methods
     *
     * @param hash hash for key key的hash值 
     * @param key the key key值 
     * @param value the value to put put的值 
     * @param onlyIfAbsent if true, don't change existing value 是否替换已有key的元素
     * @param evict if false, the table is in creation mode. 没用这个参数暂时 
     * @return previous value, or null if none 如果存在元素 就返回旧元素 如果不存在就返回null 
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
     
        // 定义变量 
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        // 如果table是null 或者长度是0 就是还没有初始化  进行初始化  n=初始化长度 
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 如果当前位置没有元素 直接新建一个node放到当前位置 
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
     
            // 如果当前位置有元素了 就需要遍历 看是不是有重复元素 有的话 替换 没有的话就追加
            Node<K,V> e; K k;
            // 先判断第一元素是不是相同 
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
                
            // 如果是树节点 
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
     
                // 普通链表
                for (int binCount = 0; ; ++binCount) {
     
                    // 如果next是null 也就是到了最后一个元素 
                    if ((e = p.next) == null) {
     
                        p.next = newNode(hash, key, value, null);
                        // 如果链表长度大于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 = e;
                }
            }
			// 存在旧值 
            if (e != null) {
      // existing mapping for key
                V oldValue = e.value;
                // 是不是替换 
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                // 返回旧值
                return oldValue;
            }
        }
        // 如果没有存在key相同的元素 modCount++
        ++modCount;
        // 如果长度大于阈值 (容量*0.75 ) resize()
        if (++size > threshold)
            resize();
        //没啥用 
        afterNodeInsertion(evict);
        return null;
    }

resize

 /**
 	 * // 初始化或者增加表格容量
     * Initializes or doubles table size. 
     * // 如果是空 初始化table  使用字段值threshold(初始化指定的容量)
     * // 如果threshold是0 那么就用默认大小16 
     * If null, allocates in  
     * accord with initial capacity target held in field threshold.
     * // 因为我们用的0.5倍扩容 所以我们的元素必须保留再相同的index 或者移动到原位置^2的位置
     * 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
     */
    final Node<K,V>[] resize() {
     
        // 定义一个变量 接收table 
        Node<K,V>[] oldTab = table;
        // oldCap原数组容量
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        // 初始化的时候 给的大小(自动转换成2的n次方)
        int oldThr = threshold;
        // 新数组容量  
        int newCap, newThr = 0;
        // 如果oldCap大于0 也就是表格已经初始化 这次是真扩容 
        if (oldCap > 0) {
     
            // 如果长度大于最大容量 MAXIMUM_CAPACITY 那么就给threshold 设置为Integer.MAX_VALUE
            // 返回旧table 不能扩容了
            if (oldCap >= MAXIMUM_CAPACITY) {
     
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            // 扩大2倍 如果小于最大容量 并且oldCap 大于默认最小容量  就设置newThr 为oldThr *2 
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold 容量双倍 
        }
       
		 // 第一次put值  并且初始化带着容量  HashMap map = new HashMap<>(16);
		 // 新table容量大小就是
        else if (oldThr > 0) 
            newCap = oldThr;
       
		 else {
                    // 如果没有填写容量 那么就用默认的DEFAULT_INITIAL_CAPACITY=16 DEFAULT_LOAD_FACTOR =0.75f
        					 // 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);
        }
        // 这里是长度的0.75 可能其他地方会用到这个值吧
        // 达到这个容量 就会resize吧(capacity * load factor)
        threshold = newThr;
       
        // 新建数组 
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        // 把table指向新数组 
        table = newTab;
        // 如果扩容前table不为空 需要把之前元素放到新table
        // 否则直接返回 
        if (oldTab != null) {
     
            // 遍历旧table 值 
            for (int j = 0; j < oldCap; ++j) {
     
                Node<K,V> e;
                // 如果元素不是null 这个一般都不是null吧 
                if ((e = oldTab[j]) != null) {
     
                    // 就表格置空 
                    oldTab[j] = null;
                    // 如果next为空 也就是只有一个元素 没有链表或红黑树  
                    // 直接hash&(新数组长度-1) Node中有put时候计算的hash值
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    // 如果是红黑树 
                    else if (e instanceof TreeNode) 
                        // 转成TreeNode 调用split
                        ((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;
                        }
                    }
                }
            }
        }
        return newTab;
    }

split

 /**	 resize的时候 红黑树操作方法 
         ((TreeNode)e).split(this, newTab, j, oldCap);
         * Splits nodes in a tree bin into lower and upper tree bins,
         * or untreeifies if now too small. Called only from resize;
         * see above discussion about split bits and indices.
         *
         * @param map 当前map
         * @param tab 新表
         * @param index 元素旧表下标
         * @param bit 旧表容量 
         */
        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
     
            // 当前树节点 
            TreeNode<K,V> b = this;
            // Relink into lo and hi lists, preserving order
            // 这是一条链表
            TreeNode<K,V> loHead = null, loTail = null;
            // 这是表示另一条链表
            TreeNode<K,V> hiHead = null, hiTail = null;
            
            int lc = 0, hc = 0;
            // 遍历树节点 
            for (TreeNode<K,V> e = b, next; e != null; e = next) {
     
                next = (TreeNode<K,V>)e.next;
                e.next = null;
                // 这里是0的话 应该是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;
                }
            }
			// 判断是转换成树 还是 链表 
            if (loHead != null) {
     
                if (lc <= UNTREEIFY_THRESHOLD)
                    tab[index] = loHead.untreeify(map);
                else {
     
                    tab[index] = loHead;
                    if (hiHead != null) // (else is already treeified)
                        loHead.treeify(tab);
                }
            }
            // 
            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);
                }
            }
        }

treeifyBin

/**
     * Replaces all linked nodes in bin at index for given hash unless
     * table is too small, in which case resizes instead.
     * 把node转换成treenode 
     */
    final void treeifyBin(Node<K,V>[] tab, int hash) {
     
        int n, index; Node<K,V> e;
        // 这里有个条件 就是表的长度要大于64 才能转红黑树 如果不大于64 那就先resize 
        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
                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);
        }
    }

你可能感兴趣的:(JAVA)