1、HashMap簡介數組
HashMap是基於哈希表實現的,每個元素是一個key-value對,其內部經過單鏈表解決衝突問題,容量不足(超過了閥值)時,一樣會自動增加。安全
HashMap是非線程安全的,只是用於單線程環境下,多線程環境下能夠採用concurrent併發包下的concurrentHashMap。多線程
HashMap 實現了Serializable接口,所以它支持序列化,實現了Cloneable接口,能被克隆。併發
HashMap存數據的過程是:函數
HashMap內部維護了一個存儲數據的Entry數組,HashMap採用鏈表解決衝突,每個Entry本質上是一個單向鏈表。當準備添加一個key-value對時,首先經過hash(key)方法計算hash值,而後經過indexFor(hash,length)求該key-value對的存儲位置,計算方法是先用hash&0x7FFFFFFF後,再對length取模,這就保證每個key-value對都能存入HashMap中,當計算出的位置相同時,因爲存入位置是一個鏈表,則把這個key-value對插入鏈表頭。性能
HashMap中key和value都容許爲null。key爲null的鍵值對永遠都放在以table[0]爲頭結點的鏈表中。spa
瞭解了數據的存儲,那麼數據的讀取也就很容易就明白了。線程
HashMap的存儲結構,以下圖所示:code
圖中,紫色部分即表明哈希表,也稱爲哈希數組,數組的每一個元素都是一個單鏈表的頭節點,鏈表是用來解決衝突的,若是不一樣的key映射到了數組的同一位置處,就將其放入單鏈表中。繼承
HashMap內存儲數據的Entry數組默認是16,若是沒有對Entry擴容機制的話,當存儲的數據一多,Entry內部的鏈表會很長,這就失去了HashMap的存儲意義了。因此HasnMap內部有本身的擴容機制。HashMap內部有:
變量size,它記錄HashMap的底層數組中已用槽的數量;
變量threshold,它是HashMap的閾值,用於判斷是否須要調整HashMap的容量(threshold = 容量*加載因子)
變量DEFAULT_LOAD_FACTOR = 0.75f,默認加載因子爲0.75
HashMap擴容的條件是:當size大於threshold時,對HashMap進行擴容
擴容是是新建了一個HashMap的底層數組,然後調用transfer方法,將就HashMap的所有元素添加到新的HashMap中(要從新計算元素在新的數組中的索引位置)。 很明顯,擴容是一個至關耗時的操做,由於它須要從新計算這些元素在新的數組中的位置並進行復制處理。所以,咱們在用HashMap的時,最好能提早預估下HashMap中元素的個數,這樣有助於提升HashMap的性能。
HashMap共有四個構造方法。構造方法中提到了兩個很重要的參數:初始容量和加載因子。這兩個參數是影響HashMap性能的重要參數,其中容量表示哈希表中槽的數量(即哈希數組的長度),初始容量是建立哈希表時的容量(從構造函數中能夠看出,若是不指明,則默認爲16),加載因子是哈希表在其容量自動增長以前能夠達到多滿的一種尺度,當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 resize 操做(即擴容)。
下面說下加載因子,若是加載因子越大,對空間的利用更充分,可是查找效率會下降(鏈表長度會愈來愈長);若是加載因子過小,那麼表中的數據將過於稀疏(不少空間還沒用,就開始擴容了),對空間形成嚴重浪費。若是咱們在構造方法中不指定,則系統默認加載因子爲0.75,這是一個比較理想的值,通常狀況下咱們是無需修改的。
另外,不管咱們指定的容量爲多少,構造方法都會將實際容量設爲不小於指定容量的2的次方的一個數,且最大值不能超過2的30次方。
2、Hashtable簡介
Hashtable一樣是基於哈希表實現的,一樣每一個元素是一個key-value對,其內部也是經過單鏈表解決衝突問題,容量不足(超過了閥值)時,一樣會自動增加。
Hashtable也是JDK1.0引入的類,是線程安全的,能用於多線程環境中。
Hashtable一樣實現了Serializable接口,它支持序列化,實現了Cloneable接口,能被克隆。
3、HashMap和Hashtable的區別
一、二者最主要的區別在於Hashtable是線程安全,而HashMap則非線程安全。Hashtable的實現方法裏面都添加了synchronized關鍵字來確保線程同步,所以相對而言HashMap性能會高一些,咱們平時使用時若無特殊需求建議使用HashMap,在多線程環境下若使用HashMap須要使用Collections.synchronizedMap()方法來獲取一個線程安全的集合(Collections.synchronizedMap()實現原理是Collections定義了一個SynchronizedMap的內部類,這個類實現了Map接口,在調用方法時使用synchronized來保證線程同步,固然了實際上操做的仍是咱們傳入的HashMap實例,簡單的說就是Collections.synchronizedMap()方法幫咱們在操做HashMap時自動添加了synchronized來實現線程同步,相似的其它Collections.synchronizedXX方法也是相似原理。
二、HashMap可使用null做爲key,不過建議仍是儘可能避免這樣使用。HashMap以null做爲key時,老是存儲在table數組的第一個節點上。而Hashtable則不容許null做爲key。
三、HashMap繼承了AbstractMap,HashTable繼承Dictionary抽象類,二者均實現Map接口。
四、HashMap的初始容量爲16,Hashtable初始容量爲11,二者的填充因子默認都是0.75。
五、HashMap擴容時是當前容量翻倍即:capacity2,Hashtable擴容時是容量翻倍+1即:capacity2+1。
六、HashMap和Hashtable的底層實現都是數組+鏈表結構實現。
七、二者計算hash的方法不一樣: Hashtable計算hash是直接使用key的hashcode對table數組的長度直接進行取模:
int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length;
HashMap計算hash對key的hashcode進行了二次hash,以得到更好的散列值,而後對table數組長度取摸:
static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } static int indexFor(int h, int length) { return h & (length-1); }
參考上海尚學堂Java文章,獲取更多內容或支持,請點擊 上海Java培訓