JDK1 7 HashMap原始碼解析

2021-10-06 14:43:39 字數 1567 閱讀 6681

在jdk1.7中,hashmap底層資料結構是由陣列和鍊錶構成,陣列存放entry物件,而entry物件是對鍊錶頭部第乙個元素的引用。arraylist中add方法是通過下標的累加進行資料的插入,hashmap則不同。

hashmap通過獲取key的hashcode值,而hashmap中構成其陣列的預設長度為16,將hashcode的值通過公式「hashcode(key)&(length-1)」,獲取下標的值,這個下標的值在不擴容的情況下永遠都在0-15之間,就是hashmap陣列的長度。在確定下標後,會以頭插法的方法插入的鍊錶中(尾插法需要乙個乙個遍歷找到最後乙個null資料,相對頭插法會損失更多的時間),然後entry物件重新指向鍊錶新的元素。若put的key相同,在鍊錶中會先進行迴圈遍歷是否存在相同的key,存在會覆蓋舊的鍊錶元素,然後put結束後會把舊的元素返回。通過採用這種「鏈址法」也解決了hash碰撞的問題。

如果指定了hashmap陣列的長度hashmap hashmap = new map(10);hashmap中會通過「rounduptopowerof2(10)「方法設定hashmap中陣列容量為》=10的冪次方,及二的四次方16,從而確定陣列容量。具體看原始碼。如果長度不是16,length-1的二進位制就全是1組成的。 10010,這樣的話有些index會永遠算出來,增加了鍊錶的長度,降低了空間的利用率,造成get(key)方法效率低下。為了讓hashcode算出來的下標雜湊開來,原始碼中對hashcode的值進行了移位運算,目的就是提高陣列的雜湊程度,提高查詢的效率。

hashmap的擴容問題。預設的陣列長度為16當插入的資料大於閾值12(16*0.75負載因子),陣列將會以兩倍擴容,擴容會建立新的兩倍陣列的長度,將舊的鍊錶循壞遍歷重新指向新的陣列中,在重新指向時,重新組成的鍊錶會更加雜湊,並且鍊錶的順序在擴容後會完全相反,然後將建立的新陣列重新賦值給舊的陣列,完成hashmap的擴容。然而,當多個執行緒同時執行hashmap的擴容時,因為是頭插法有可能會導致鍊錶的死迴圈,導致cpu被吃滿,這就是hashmap是執行緒不安全的原因。

hashmap擴容會帶來rehash操作,根據不同虛擬機器引數"jdk.map.althashing.threshold"的設定,如果容量大於等於這個值,就會造成rehash,rehash會讓陣列的鍊錶雜湊性更好,提高hashmap的put方法查詢效率,但是rehash也會消耗記憶體。這個可以根據需要來修改虛擬機器的值。

modcount++,在hashmap原始碼中會造成異常(currentmodificationexception)。無論是put還是add都會使modcount++。

iterator i = hashmap.keyset().iterator();

while(i.hasnext())

}

這是一種fastfail(快速失敗)方式,提高容錯率,因為hashmap本身就不是執行緒安全的,不管刪除還是新增,都會對modcount造成累加,在多執行緒當中多個執行緒同時操作(查詢、刪除)同乙個hashmap的時候,就會丟擲currentmodificationexception,結束下面的**執行。

JDK1 7HashMap原始碼解析

hashmap已經看了很多篇文章了,今天還是自己解析一遍吧。我先大致介紹下hashmap的內部結構再跟著原始碼解讀一番 眾所周知hashmap的內部就是乙個雜湊表 什麼是雜湊表?如果我們利用陣列可隨機訪問的特性,將要存入的鍵通過一種雜湊演算法轉換成乙個數字,並把這個數字轉換成陣列的下標,然後將鍵和他...

jdk1 7 hashMap原始碼學習

預設初始化長度 static final int default initial capacity 16 最大長度 static final int maximum capacity 1 30 預設載入因子 size capacity loadfactor static final float de...

JDK1 7的HashMap原始碼解讀

default initial capacity 初始化容量,中為1 4 即為16。為什麼要這樣寫呢?maximum capacity 最大容量,中衛1 30 即為2的30次冪。30次冪的原因是 改屬性為int型別,int型別最大為4個位元組,共32個二進位制位,理論上可以向左移動31次,即31次冪...