HashMap原始碼學習筆記

2021-07-04 15:45:59 字數 4060 閱讀 9633

hashmap的底層主要是基於陣列和鍊錶來實現的,它之所以有相當快的查詢速度主要是因為它是通過計算雜湊碼來決定儲存的位置。hashmap中主要是通過key的hashcode來計算hash值的,只要hashcode相同,計算出來的hash值就一樣。如果儲存的物件對多了,就有可能不同的物件所算出來的hash值是相同的,這就出現了所謂的hash衝突。學過資料結構的同學都知道,解決hash衝突的方法有很多,hashmap底層是通過鍊錶來解決hash衝突的。

hashmap其實就是乙個entry陣列,entry物件中包含了鍵和值,其中next也是乙個entry物件,它就是用來處理hash衝突的,形成乙個鍊錶。陣列的每個元素都是乙個單鏈表的頭節點,鍊錶是用來解決衝突的,如果不同的key對映到了陣列的同一位置處,就將其放入單鏈表中。

hashtable 與 hashmap類似,但是主要有6點不同。  1.

hashtable的方法是同步的,hashmap未經同步,所以在多執行緒場合要手動同步hashmap這個區別就像vector和arraylist一樣。  

2. hashtable不允許null值,key和value都不可以

,hashmap允許null值,key和value都可以。hashmap允許key值只能由乙個null值,因為hashmap如果key值相同,新的key, value將替代舊的。  

3.hashtable有乙個contains(object value)功能和containsvalue(object value)功能一樣。  

4.hashtable使用enumeration,hashmap使用iterator。  

5.hashtable中hash陣列預設大小是11,增加的方式是 old*2+1。

hashmap中hash陣列的預設大小是16,而且一定是2的指數。

1、hashtable的方法是同步的,hashmap未經同步

hashtable 提供的幾個主要方法,包括

get(), put(), remove() 等。每個方法本身都是 

synchronized 的,會對整個物件進行加鎖操作,

不會出現兩個執行緒同時對資料進行操作的情況,因此保證了執行緒安全性,但是也大大的降低了執行效率。

2、hashmap中hash陣列的預設大小是16,而且一定是2的指數

staticintindexfor(inth,intlength)   

將key的二次hash值,與長度減一進行與操作,這一步可謂經典,通常我們會用取模的方式來定位陣列中的某個位置,我們首先想到的就是把hashcode對陣列長度取模運算,這樣一來,元素的分布相對來說是比較均勻的。但是,「模」運算的消耗還是比較大的,能不能找一種更快速,消耗更小的方式那?hashmap用這種方法,而且length即capacity的值,面capacity又是2的倍數,減1之後,表示成二進位制就全部是1了,那麼與全部為1的乙個數進行與操作,速度會大大提公升了。這就是為什麼"capacity的值是2的倍數"

3、hashmap解決衝突的辦法

1)再雜湊法

2)拉鍊發

publicv put(k key, v value)   

}  modcount++;  

addentry(hash, key, value, i);  

returnnull;  

}  

根據key的hash值得到這個元素在陣列中的位置(即下標),然後就可以把這個元素放到對應的位置中了。如果這個元素所在的位子上已經存放有其他元素了,那麼在同乙個位子上的元素將以鍊錶的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。從hashmap中get元素時,首先計算key的hashcode,找到陣列中對應位置的某一元素,然後通過key的equals方法在對應位置的鍊錶中找到需要的元素。

呼叫int hash = hash(key.hashcode());這是hashmap的乙個自定義的hash,在key.hashcode()基礎上進行

二次hash

。再雜湊法能夠解決開放位址法的聚集現象,但是卻增加了時間複雜度。

staticinthash(inth)   

改進傳統的hash方法,而且盡量保證key的每一位都會影響到最後的hash值,以達到減少hash衝突的目的.

4、hashmap的建構函式

if(initialcapacity < 0)  

thrownewillegalargumentexception("illegal initial capacity: " +  

initialcapacity);  

if(initialcapacity > maximum_capacity)  

initialcapacity = maximum_capacity;  

if(loadfactor <= 0 || float.isnan(loadfactor))  

thrownewillegalargumentexception("illegal load factor: " +  

loadfactor);  

// find a power of 2 >= initialcapacity

intcapacity = 1;  

while(capacity < initialcapacity)  

capacity <<= 1; 

this.loadfactor = loadfactor;  

threshold = (int)(capacity * loadfactor);  

table =newentry[capacity];  

init();  

}  

loadfactor :載入因子,載入因子與hashmap resize有關。預設為0.75

capacity:容器大小,預設值為16 其大小為上面所說的資料結構中陣列的長度。

table:即為上面資料結構圖中,x方向的陣列(transient entry table;)

threshold :resize的臨界值,即當hashmap中無素個數達到該值時,hashmap就會呼叫其resize方法,重新擴充大小。

while (capacity < initialcapacity)

capacity <<= 1;  

這段**說明什麼:capacity的值是2的倍數,即使設定初始值是1000,hashmap也自動會將其設定為1024。

5、雜湊表解決衝突的常用方法

1)開放位址法

2)拉鍊法

3)再雜湊法

拉鍊法的優點 ,與開放定址法相比,拉鍊法有如下幾個優點: 

①拉鍊法處理衝突簡單,且

無堆積現象,即非同義詞決不會發生衝突,因此平均查詢長度較短;

②由於拉鍊法中各煉表上的結點空間是動態申請的,故它更適合於造表

前無法確定表長的情況;

③開放定址法為減少衝突,要求裝填因子α較小,故當結點規模較大時會浪費很多空間。而拉鍊法中可取α≥1,且結點較大時,拉鍊法中增加的指標域可忽略不計,因此節省空間;

④在用 拉鍊法構造的雜湊表中,刪除結點的操作易於實現。只要簡單地刪去鍊錶上相應的結點即可。而對開放位址法構造的雜湊表,刪除結點不能簡單地將被刪結 點的空間置為空,否則將截斷在它之後填人雜湊表的同義詞結點的查詢路徑。這是因為各種開放位址法中,空位址單元(即開放位址)都是查詢失敗的條件。因此在 用開放位址法處理衝突的雜湊表上執行刪除操作,只能在被刪結點上做刪除標記,而不能真正刪除結點。

拉鍊法的缺點 

拉鍊法的缺點是:指標需要額外的空間,故當

結點規模較小時,開放定址法較為節省空間,而若將節省的指

HashMap原始碼學習筆記

hashmap 資料結構 初始容量 static final int de t initial capacity 1 4 最大容量 static final int maxinum capacity 1 30 擴容因子 static final float default load factor 0...

學習筆記HashMap原始碼學習

hashmap hashmapextends abstractmap implements map,cloneable,serializable 繼承abstractmap類,實現頂層介面map介面 int default initial capacity 1 4 預設容量為16 int maxim...

HashMap原始碼筆記

預設的初始化容量為16 static final int default initial capacity 1 4 最大的容量,容量的值必須是2的冪並且小於最大的容量,最大值為2的30次方 static final int maximum capacity 1 30 載入因子預設值為0.75 sta...