HaspMap的put原理解析

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

里面的hash方法

// 作用
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
				   // tab 引用当前hashMap的散列表
				   // p: 表示当前散列表的元素
				   // n: 表示散列表数组的长度
				   // i: 表示路由寻址的结果
				   
    Node<K,V>[] tab; Node<K,V> p; int n, i;
	// 延迟初始化逻辑,第一次调用putval时会初始化hashMap对象中的最耗内存的散列表
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
	// 最简单一种情况:寻址找到的桶位,刚好是null,这时就可以直接扔进去就可以了
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
		// e:是node临时元素,找到了一个要与当前插入的key-value一致的key元素
		// k:表示临时的一个key
        Node<K,V> e; K k;
		// 表示桶中该元素,与你当前插入的元素的key完全一致,表示后续需要进行替换操作
		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 {
			// 链表的处理,而且链表的头元素与我们要插入的key不一致
            for (int binCount = 0; ; ++binCount) {
				// 条件成立的话,说明迭代到最后一个元素了,也没有找到一个与要插入的key一直的node
                if ((e = p.next) == null) {
					// 就当前最后一个节点插入数据
                    p.next = newNode(hash, key, value, null);
					// 判斷鏈表d的长度是否大于8,>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;
        }
    }
	// 表示散列表结构被修改的次数,替换的不算
    ++modCount;
	// 插入新元素,size自增,如果自增后的值大于扩容阈值,则触发扩容
    if (++size > threshold)
		// 扩容的机制
        resize();
    afterNodeInsertion(evict);
    return null;
}

三、扩容机制

		// 为了解决hash冲突导致的链化影响查询效率的问题,扩容会缓解改问题
final Node<K,V>[] resize() {
// oldTab 引用扩容前的hash表
Node<K,V>[] oldTab = table;
// oldCap 表示扩容之前table数组的长度
int oldCap = (oldTab == null) ? 0 : oldTab.length;
// 扩容之前的扩容阈值,触发本次扩容的阈值
int oldThr = threshold;
// newCap 扩容之后table数组的大小
// newThr 扩容之后,下次在触发扩容的条件
int newCap, newThr = 0;
// 条件如果成立说明 hashMap中的散列表已经初始化过了,是一次正常扩容
if (oldCap > 0) {
	// 扩容之前的table数组大小已经达到 最大阈值后,则不扩容,且设置扩容条件为int最大值
    if (oldCap >= MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return oldTab;
    }
	// 数据左移动一位,数据翻一倍数据 并赋值给newCap newCap小于数组最大阈值 且 扩容之前的阈值 >= 16
	// 这种情况下 则下一次扩容的阈值 等于当前阈值翻倍
    else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
             oldCap >= DEFAULT_INITIAL_CAPACITY)
        newThr = oldThr << 1; // double threshold
}
// oldCap == 0,说明hashMap的散列表是null
// new HashMap(initCap,loadFactor)
// new HashMap(initCap)
// new HashMap(map)
else if (oldThr > 0) // initial capacity was placed in threshold
    newCap = oldThr;
else {               // zero initial threshold signifies using defaults
   // 什么值都没有设置
   // oldCap == 0,oldThr=0
   // new HashMap()
    newCap = DEFAULT_INITIAL_CAPACITY;
    newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// 什么条件下 DEFAULT_INITIAL_CAPACITY > oldCap 就没有读newThr赋值
// newThr为零时,通过newCap和loadFactor计算出一个newThr
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;
	// 本次扩容之前 table不为null
    if (oldTab != null) {
		// 遍历以前旧数组中的长度
        for (int j = 0; j < oldCap; ++j) {
			//  当前node节点
            Node<K,V> e;
			// 说明当前桶中有数据,但是数据具体是 单个数据 还是链表  还是数结构
            if ((e = oldTab[j]) != null) {
				// 方便jvm 中GC进行回收
                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;
                    }
                }
            }
        }
    }
    return newTab;
}

版权声明:本文为weixin_43863463原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。