
1、紅黑樹特性(缺一不可):
(1)、每個節點要么是紅色要么是黑色。
(2)、根節點是黑色。
(3)、所有葉子節點都是黑色(葉子節點為NIL或者NULL節點)。
(4)、不存在兩個連續的紅色節點。
(5)、任意節點(包含跟節點)到其葉子節點的所有路徑都包含相同數目的黑色節點。
2、為什么HashMap中使用紅黑樹而不使用AVL樹呢?
紅黑樹被稱為弱AVL樹,犧牲了嚴格的高度平衡的優越條件為代價(紅黑樹左右子樹的高度差不超過一倍即可)使其能夠以O(log2 n)的時間復雜度進行搜索、插入、刪除操作;此外,由於它的設計,任何不平衡都會在三次旋轉之內解決。因為HashMap的使用場景中插入和刪除操作是非常頻繁的,所以在HashMap中使用了紅黑樹。
3、紅黑樹RBT與平衡二叉樹AVL比較:
(1)、紅黑樹和AVL樹類似,都是在進行插入和刪除操作時通過特定操作保持二叉查找樹的平衡,從而獲得較高的查找性能。
(2)、紅黑樹和AVL樹的區別在於它使用顏色來標識節點的高度,它所追求的是局部平衡而不是AVL樹中的非常嚴格的平衡。
(3)、AVL 樹比紅黑樹更加平衡,但AVL樹在插入和刪除的時候也會存在大量的旋轉操作。所以當你的應用涉及到頻繁的插入和刪除操作,切記放棄AVL樹,選擇性能更好的紅黑樹;當然,如果你的應用中涉及的插入和刪除操作並不頻繁,而是查找操作相對更頻繁,那么就優先選擇 AVL 樹進行實現。
二、HashMap元素插入過程及一些參數的詳解
1、首先,需要了解HashMap源碼中幾個重要的參數:
DEFAULT_INITIAL_CAPACITY:默認初始化大小
MAXIMUM_CAPACITY:最大容量
DEFAULT_LOAD_FACTOR:默認的負載因子
TREEIFY_THRESHOLD:鏈表轉化為紅黑樹的閾值(包含)
UNTREEIFY_THRESHOLD:紅黑樹轉化為鏈表的閾值(包含)
MIN_TREEIFY_CAPACITY:當數組大小小於該值時,不進行鏈表向紅黑樹的轉化,而是進行擴容
2、HashMap存儲元素過程:
(1)圖中剛開始有計算 key 的 hash 值的設計?
拿到 key 的 hashCode,並將 hashCode 的高16位和 hashCode 進行異或(XOR)運算,得到最終的 hash 值。
(2)為什么要將 hashCode 的高16位參與運算?
主要是為了在 table 的長度較小的時候,讓高位也參與運算,並且不會有太大的開銷。
(3)為什么鏈表轉紅黑樹的閾值是8?
我們平時在進行方案設計時,必須考慮的兩個很重要的因素是:時間和空間。對於 HashMap 也是同樣的道理,簡單來說,閾值為8是在時間和空間上權衡的結果。紅黑樹節點大小約為鏈表節點的2倍,在節點太少時,紅黑樹的查找性能優勢並不明顯,付出2倍空間的代價不值得。理想情況下,使用隨機的哈希碼,節點分布在 hash 桶中的頻率遵循泊松分布,按照泊松分布的公式計算,鏈表中節點個數為8時的概率為 0.00000006,這個概率足夠低了,並且到8個節點時,紅黑樹的性能優勢也會開始展現出來,因此8是一個較合理的數字。
(4)HashMap 的默認初始容量是多少?HashMap 的容量有什么限制嗎?
默認初始容量是16。HashMap 的容量必須是2的N次方,HashMap 會根據我們傳入的容量計算一個大於等於該容量的最小的2的N次方,例如傳 9,容量為16。
(5)為什么 HashMap 的容量必須是 2 的 N 次方?
計算索引位置的公式為:(n - 1) & hash,當 n 為 2 的 N 次方時,n - 1 為低位全是 1 的值,此時任何值跟 n - 1 進行 & 運算的結果為該值的低 N 位,達到了和取模同樣的效果,實現了均勻分布。實際上,這個設計就是基於公式:x mod 2^n = x & (2^n - 1),因為 & 運算比 mod 具有更高的效率。當 n 不為 2 的 N 次方時,hash 沖突的概率明顯增大。
(6)為什么HashMap的負載因子默認為0.75?
在HashMap的類注釋上有如圖一段解釋:大致意思是說負載因子是0.75的時候,空間利用率比較高,而且避免了相當多的Hash沖突,使得底層的鏈表或者是紅黑樹的高度比較低,提升了空間效率。