標籤:dex alt ati 轉變 變化 ret 下標越界 class 索引
目錄
content
HashMap 的資料結構:
- 數組 + 鏈表(Java7 之前包括 Java7)
- 數組 + 鏈表 + 紅/黑樹狀結構(從 Java8 開始)
PS:這裡的《紅/黑樹狀結構》與鏈表都是鏈式結構。
HashMap 內部維護了一個數組,數組中存放鏈表的鏈首或紅/黑樹狀結構的樹根。
當鏈表長度超過 8 時,鏈表就轉換為紅/黑樹狀結構,利用紅/黑樹狀結構快速增刪改查的特點提高 HashMap 的效能;在紅/黑樹狀結構結點數量小於 6 時,紅/黑樹狀結構轉變為鏈表。
下面分別為上面兩種資料結構的圖示:
【定位演算法】
增加、尋找、刪除等操作都需要先定位到 table 數組的某個索引處。
定位演算法為三步:取 key 的 hashCode 值、高位元運算、模數運算得到索引位置。(代碼如下)
static final int hash(Object key) { int h; // h = key.hashCode() 第一步 取 hashCode 值 // h ^ (h >>> 16) 第二步 高位參與運算 Java8 最佳化了高位演算法,最佳化原理忽略 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}// java7 中這是一個單獨的方法,java8 沒有了這個方法但是原理依舊static int indexFor(int h, int length) { return h & (length-1); // hash(key) & (length-1) 第三步 模數}
模數運算h & (length -1)的結果最大值為 length -1,不會出現數組下標越界的情況。
為什麼要做高位元運算?
如果 hashCode 值都大於 length,而且這些 hashCode 的低位變化不大,就會出現很多衝突,舉個例子:
- 假設數組的初始化容量為 16(10000),則 length -1 位 15(1111)。
- 假設有幾個對象的 hashCode 分別為 1100 10010、1110 10010、11101 10010,如果不做高位元運算,直接使用它們做模數運算的結果將是一致的。
如果所有元素中多數元素屬於這種情況,將會導致元素分布不均勻,而對 hashCode 進行高位元運算能解決這個問題,使高位對低位造成影響改變低位的值,從而變相地使高位也參與運算。
append
【Q】負載因子與效能的關係
負載因子預設值為0.75,意味著當數組實際填充量佔比達到3/4時就該擴容了。
負載因子越大,擴容次數必然越少,數組的長度越小,減少了空間開銷;這就會導致 hash 碰撞越多,增加查詢成本。
預設值0.75在時間和空間成本上尋求一種折衷。
【Q】為什麼要擴容
因為隨著元素量的增大,hash 碰撞的機率越來越大,雖然使用鏈地址法能夠解決儲存問題,但是長長的鏈表會讓 HashMap 失去快速檢索的優勢,而擴容能解決這個問題。
HashMap 的資料結構