HashMap和HashTable簡介和區別

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培訓

相關文章
相關標籤/搜索