這篇文章是記錄本身分析 Java 8 的 HashMap
源碼時遇到的疑問和總結,在分析的過程當中筆者把遇到的問題都記錄下來,而後逐一擊破,若是有錯誤的地方,但願讀者能夠指正,筆者感激涕零。java
The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created.git
initial capacity 指定了 HashMap 內部的 hash table 的初始化容量,能夠經過構造函數指定,默認的初始化容量爲 16github
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
複製代碼
The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.數組
能夠看到 load factor 是用於肯定 hash table 什麼時候擴容的重要參數之一。安全
Iteration over collection views requires time proportional to the "capacity" of the HashMap instance (the number of buckets) plus its size (the number of key-value mappings). Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.數據結構
An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets. app
查看源碼描述函數
描述主要提到三個問題:性能
筆者通過一番測試,在 hash 均勻分佈的前提下,數據量從 100 ~ 千萬級,initial capacity 設置從 1 到千萬不等,發現這個值的大小對迭代性能和 rehash 的影響不是很大(到千萬級別纔會有稍微明顯的效率問題),緣由是擴容時,每次都會 double threshold,有效避免了高頻率的擴容動做;對迭代性能的影響也很是低(10W數據,1億初始容量,毫秒級別影響)。測試
默認 0.75 便可,過低可能會致使頻繁的擴容,而且有可能致使 java.lang.OutOfMemoryError: Java heap space
When
putting data && (hashtable.length == 0 || entries.length >= threshold)
Then resize() 複製代碼
觸發 resize 的時候,會發生一次擴容,並隨 rehash。
擴容時會重建 hash table,因爲新 hash table 的容量 = 舊 hash table 容量左移一位,所以須要 rehash,以確保新的 hash 落在 [0, new_capacity) 區間內。
hash
落入 [0, capacity) 區間?(capacity - 1) & (hashcode ^ (hashcode >>> 16))
capacity 取值範圍: 2^(N+) - 1,0 < (N+) < 31。
在 hashCode 均勻分佈的前提下(Object.hashCode() 默認爲每一個對象生成不一樣的 hash code,具體原理能夠看 java doc),hashcode ^ (hashcode >>> 16)
能夠下降 hash 衝突的概率(相對於 (capacity - 1) & hashcode
),原理是混合原始哈希碼的高位和低位,以此來加大低位的隨機性;(capacity - 1) & new_hash 能夠保證計算出來的 index 落入 [0, capacity)。
經過覆寫 Object##hashCode()
方法便可自定義 hashCode,就能夠模擬 hash 碰撞,例如隨機 hashCode 或固定 hashCode。
Given
TREEIFY_THRESHOLD = 8
When
hashCount >= TREEIFY_THRESHOLD Then treeifyBin(bin) 複製代碼
當出現同一個 hash 達到 8 次碰撞,就會從鏈表轉換成紅黑樹。
hash table 本質上是一個數組 + 鏈表或紅黑樹的數據結構, hash table 經過創建 hash 到數據節點的映射關係,巧妙的達成 O(1) 的檢索效率。其中每一個鏈表保存着 hash 衝突(key 不相等,hash 值相等)的數據項,默認狀況下,當達到 8 個衝突時,鏈表就會轉換成紅黑樹,轉換以後還伴隨着 O(n) -> O(log n) 的效率提高,不過這種狀況出現的機率很低(在分散的 hash code 的前提下,相同的 hash 值產生 8 次衝突的機率僅爲千萬分之 6),不過能夠經過人爲模擬重現這種狀況。
頻繁的 hash 衝突是致使 HashMap 性能降低的罪魁禍首,當同一個 hash 值衝突達到 8 次及以上時, 此時 rbtree 可能須要常常經過左旋、右旋和着色來保持自身平衡,這個代價之大跟同一個 hash 值的衝突次數成正比,所以須要維護好重寫的 hashCode 方法,使 hash code 儘量分散。
用於提升哈希衝突條件下的性能,同時去除了之前版本遺留下來的問題,具體能夠看這裏 openjdk.java.net/jeps/180
紅黑樹(英語:Red–black tree)是一種自平衡二叉查找樹,是在計算機科學中用到的一種數據結構,典型的用途是實現關聯數組。它在1972年由魯道夫·貝爾發明,被稱爲"對稱二叉B樹",它現代的名字源於Leo J. Guibas和Robert Sedgewick於1978年寫的一篇論文。紅黑樹的結構複雜,但它的操做有着良好的最壞狀況運行時間,而且在實踐中高效:它能夠在 O(log n)時間內完成查找,插入和刪除,這裏的 n 是樹中元素的數目。
上面是維基百科中的定義,同時紅黑樹還有 5 個性質,紅黑樹是每一個節點都帶有顏色屬性的二叉查找樹,顏色爲紅色或黑色。在二叉查找樹強制通常要求之外,對於任何有效的紅黑樹增長了以下的額外要求:
時間敏感型程序。
Linux 內核和 epoll 系統調用實現中使用的徹底公平調度程序使用紅黑樹。
HashMap
經過這個機制確保迭代時,若是修改 map 的結構(增,刪),一經發現則當即拋出 ConcurrentModificationException
,而不是等到將來的某個時刻再通知異常,能夠經過這個機制來捕獲程序的一些 BUG.
在 HashMap 之上包裝多一層,而且使用 synchronized
同步鎖鎖定 HashMap 實例的全部公開接口,Collections#synchronizedMap
已經提供了這樣一種實現。
經過閱讀源碼、查閱資料和動手驗證,才知道 HashMap 的知識點那麼多,不過都啃得差很少以後,就會以爲知識點不多(應該還有遺漏掉的或者不嚴謹的狀況),確實讓本身學到了不少以前不知道的知識點。
在接下來的文章中,筆者會經過 TDD (測試驅動開發)來記錄如何實現一個精簡版的 HashMap,避免一看就會,一作就廢的尷尬局面。