1. 程式人生 > >jdk1.8中hashtable原始碼分析

jdk1.8中hashtable原始碼分析

注:基於JDK 1.8.0_131原始碼為例進行分析

hashtable的結構圖

hashtable採用桶位+連結串列結構實現。
這裡寫圖片描述

hashtable的實現

採用的是“桶位”,即一個Entry陣列實現:
這裡寫圖片描述
Entry節點的實現:主要包括了key、value以及key的雜湊值和next指向想一個節點。
這裡寫圖片描述
說明:

  • 程式碼塊1:為Entry節點中包含的key、value、hash、next。
  • 程式碼塊2:為Entry的建構函式,即把新節點接在了next連結串列的開頭部分。

相關引數規定

這裡寫圖片描述
其中,threshold表示table陣列擴容的節點數閾值;loadFactory為負載因子。

預設的陣列大小,及負載因子:
這裡寫圖片描述
Hasttable的預設容量(陣列大小)為11,預設的負載因子為0.75。同時由程式碼塊2可知陣列擴容的閾值threshold=capacity*loadFactory。
注意:hashtable中的陣列容量capacity沒有限制為2的n次方,取任何大於0的數都行

Hashtable的put操作實現

這裡寫圖片描述
說明:

  • 程式碼塊1:hashtable之所以是執行緒安全的,原因為在put和get方法上使用synchronized關鍵字進行修飾。
  • 程式碼塊2:限制了value不能為null
  • 程式碼塊3:由於直接使用key.hashcode(),而沒有向hashmap一樣先判斷key是否為null,所以key為null時,呼叫key.hashcode()會出錯,所以hashtable中key也不能為null
  • 程式碼塊4:hashtable中對hash值進行定址的方法為hash%陣列長度(與hashmap不同,所以不要求陣列長度必須為2的n次方)。
  • 程式碼塊5:entry=table[index]
  • 程式碼塊6:遍歷table[index]所連線的連結串列,查詢是否已經存在key與需要插入的key值相同的節點,如果存在則直接更新value,並返回舊的value。
  • 程式碼塊7:如果table[index]所連線的連結串列上不存在相同的key,則通過addEntry()方法將新節點載入連結串列的開頭

這裡寫圖片描述
說明:

  • 程式碼塊1:如果加入新節點後,hashtable中元素的個數超過了閾值threshold,則利用rehash()對陣列進行擴容。
  • 程式碼塊2:e=table[index],並將新節點加在了e節點的1前面,最後table[index]=e。相當於把把新節點放在table[index]位置,即整個連結串列的首部
    Hashtable中陣列的擴容:
    這裡寫圖片描述
    即:每次擴容時,容量都變成了原來的2倍。

Hashtable的get操作實現

這裡寫圖片描述
說明:

  • 程式碼塊1:hashtable之所以是執行緒安全的,原因為在put和get方法上使用synchronized關鍵字進行修飾。
  • 程式碼塊2:進行hash值的定址,找到在table陣列中的位置index.
  • 程式碼塊3:遍歷table[index]連結串列,找到key值相同的節點的value返回,注意同樣在該過程中使用到了key的equal方法,所以key被應用與hashtable時不僅要實現hashcode方法還有實現equal方法

Hashtable和hashmap的區別總結

1、 hashmap中key和value均可以為null,但是hashtable中key和value均不能為null。

2、 hashmap採用的是陣列(桶位)+連結串列+紅黑樹結構實現,而hashtable中採用的是陣列(桶位)+連結串列實現。

3、 hashmap中出現hash衝突時,如果連結串列節點數小於8時是將新元素加入到連結串列的末尾,而hashtable中出現hash衝突時採用的是將新元素加入到連結串列的開頭。

4、 hashmap中陣列容量的大小要求是2的n次方,如果初始化時不符合要求會進行調整,而hashtable中陣列容量的大小可以為任意正整數。

5、 hashmap中的定址方法採用的是位運算按位與,而hashtable中定址方式採用的是求餘數。

6、 hashmap不是執行緒安全的,而hashtable是執行緒安全的,hashtable中的get和put方法均採用了synchronized關鍵字進行了方法同步。

7、 hashmap中預設容量的大小是16,而hashtable中預設陣列容量是11。