1. 程式人生 > >深入理解HashMap(精華必看)

深入理解HashMap(精華必看)

3、hashmap的resize 
       當hashmap中的元素越來越多的時候,碰撞的機率也就越來越高(因為陣列的長度是固定的),所以為了提高查詢的效率,就要對hashmap的陣列進行擴容,陣列擴容這個操作也會出現在ArrayList中,所以這是一個通用的操作,很多人對它的效能表示過懷疑,不過想想我們的“均攤”原理,就釋然了,而在hashmap陣列擴容之後,最消耗效能的點就出現了:原陣列中的資料必須重新計算其在新陣列中的位置,並放進去,這就是resize。 

         那麼hashmap什麼時候進行擴容呢?當hashmap中的元素個數超過陣列大小*loadFactor時,就會進行陣列擴容,loadFactor的預設值為0.75,也就是說,預設情況下,陣列大小為16,那麼當hashmap中元素個數超過16*0.75=12的時候,就把陣列的大小擴充套件為2*16=32,即擴大一倍,然後重新計算每個元素在陣列中的位置,而這是一個非常消耗效能的操作,所以如果我們已經預知hashmap中元素的個數,那麼預設元素的個數能夠有效的提高hashmap的效能。比如說,我們有1000個元素new HashMap(1000), 但是理論上來講new HashMap(1024)更合適,不過上面annegu已經說過,即使是1000,hashmap也自動會將其設定為1024。 但是new HashMap(1024)還不是更合適的,因為0.75*1000 < 1000, 也就是說為了讓0.75 * size >1000, 我們必須這樣new HashMap(2048)才最合適,既考慮了&的問題,也避免了resize的問題。 


4、key的hashcode與equals方法改寫 

在第一部分hashmap的資料結構中,annegu就寫了get方法的過程:首先計算key的hashcode,找到陣列中對應位置的某一元素,然後通過key的equals方法在對應位置的連結串列中找到需要的元素。所以,hashcode與equals方法對於找到對應元素是兩個關鍵方法。 

Hashmap的key可以是任何型別的物件,例如User這種物件,為了保證兩個具有相同屬性的user的hashcode相同,我們就需要改寫hashcode方法,比方把hashcode值的計算與User物件的id關聯起來,那麼只要user物件擁有相同id,那麼他們的hashcode也能保持一致了,這樣就可以找到在hashmap陣列中的位置了。如果這個位置上有多個元素,還需要用key的equals方法在對應位置的連結串列中找到需要的元素,所以只改寫了hashcode方法是不夠的,equals方法也是需要改寫滴~當然啦,按正常思維邏輯,equals方法一般都會根據實際的業務內容來定義,例如根據user物件的id來判斷兩個user是否相等。 
在改寫equals方法的時候,需要滿足以下三點: 
(1) 自反性:就是說a.equals(a)必須為true。 
(2) 對稱性:就是說a.equals(b)=true的話,b.equals(a)也必須為true。 
(3) 傳遞性:就是說a.equals(b)=true,並且b.equals(c)=true的話,a.equals(c)也必須為true。 
通過改寫key物件的equals和hashcode方法,我們可以將任意的業務物件作為map的key(前提是你確實有這樣的需要)。 

總結: 

    本文主要描述了HashMap的結構,和hashmap中hash函式的實現,以及該實現的特性,同時描述了hashmap中resize帶來效能消耗的根本原因,以及將普通的域模型物件作為key的基本要求。尤其是hash函式的實現,可以說是整個HashMap的精髓所在,只有真正理解了這個hash函式,才可以說對HashMap有了一定的理解。