Преглед изворни кода

hashmap源码解读1.7与1.8的差别

Sun1040084806 пре 3 година
родитељ
комит
aede2cf121
1 измењених фајлова са 115 додато и 0 уклоњено
  1. 115 0
      后端/JAVA高阶/HashMap/第二节课HashMap源码分析.md

+ 115 - 0
后端/JAVA高阶/HashMap/第二节课HashMap源码分析.md

@@ -122,3 +122,118 @@ if (++size > threshold)
 if (++size > threshold)
 resize();
 
+## 分析hashmap扩容原理
+
+扩容的时候以2的幂次方扩容,将原来的table中存放的key循环遍历存入新的table中
+误区:不会重新计算hash值,重新计算index(为什么链表会缓存hash值)
+
+### 1.7版本的扩容
+
+```
+void transfer(Entry[] newTable, boolean rehash) {
+    int newCapacity = newTable.length;
+    // 遍历原来宿主中所有的链表
+    for (Entry<K,V> e : table) {
+    // 如果存在链表
+      while(null != e) {
+        Entry<K,V> next = e.next;
+        // 是否需要重新计算hash
+        if (rehash) {
+          e.hash = null == e.key ? 0 : hash(e.key);
+        }
+        // 通过key值的hash值和新数组的大小算出在当前数组中的存放位置
+        int i = indexFor(e.hash, newCapacity);
+        // 会发生死循环
+        // 这里使用了头插法
+        e.next = newTable[i];
+        newTable[i] = e;
+        e = next;
+      }
+    }
+}
+```
+hashmap1.7	16×2=32
+hashmap1.8	16<<1=32
+
+线程安全问题,多线程访问共享全局变量
+什么情况下产生死循环?
+因为table多线程共享,newTable是线程私有的。会导致C->A->C
+如果查询当前不存在的值会导致死循环,导致CPU飙升。
+为什么JDK官方不承认JDK1.7的BUG?
+多线程才会引起BUG,单线程不会有BUG
+多线程推荐使用并发的多线程hashmap
+
+### 1.8版本的扩容
+
+```
+// 查找所有链表
+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;
+			}
+			// 这里肯定不会有冲突
+			// 新的值一定比旧的值大oldCap
+			if (hiTail != null) {
+				hiTail.next = null;
+				newTab[j + oldCap] = hiHead;
+			}
+	}
+```
+
+1.8解决死循环问题?
+将一个链表通过与运算拆分成两个链表存放在新的table中。
+
+## 为什么加载因子不是1,而是0.75?
+
+如果加载因子越大,空间利用率越高,冲突发生概率较大。反之,加载因子越小,空间利用率越低,冲突发生概率较小。综合泊松分布,得出0.75效率最高
+
+## 如何存放一万条key效率最高?
+
+会触发10次扩容!
+推荐指定hashmap集合的初始化容量,(initialCapacity) = 存储元素个数 / 加载因子 + 1
+
+## hashmap7与8的区别
+
+1. 1.7基于数组加链表,采用头插法
+2. 1.8基于数组加链表加红黑树,采用尾插法
+	A. 能够降低key对应的index冲突概率,提高查询概率
+	B. 会拆分链表