HashMap和Hashtable的區(qū)別

HashMap和Hashtable的比較是Java面試中的常見問題吊档,用來考驗程序員是否能夠正確使用集合類以及是否可以隨機(jī)應(yīng)變使用多種思路解決問題秒拔。HashMap的工作原理穷娱、ArrayList與Vector的比較以及這個問題是有關(guān)Java 集合框架的最經(jīng)典的問題谷市。Hashtable是個過時的集合類亡嫌,存在于Java API中很久了嚎于。在Java 4中被重寫了,實現(xiàn)了Map接口挟冠,所以自此以后也成了Java集合框架中的一部分于购。Hashtable和HashMap在Java面試中相當(dāng)容易被問到,甚至成為了集合框架面試題中最常被考的問題知染,所以在參加任何Java面試之前肋僧,都不要忘了準(zhǔn)備這一題。

這篇文章中控淡,我們不僅將會看到HashMap和Hashtable的區(qū)別嫌吠,還將看到它們之間的相似之處。

HashMap和Hashtable的區(qū)別

HashMap和Hashtable都實現(xiàn)了Map接口掺炭,但決定用哪一個之前先要弄清楚它們之間的分別辫诅。主要的區(qū)別有:線程安全性,同步(synchronization)涧狮,以及速度炕矮。

  • 繼承的父類不同
    Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類勋篓。但二者都實現(xiàn)了Map接口吧享。

  • 對null對象的支持不同
    HashMap是支持null鍵和null值的魏割,而HashTable在遇到null時譬嚣,會拋出NullPointerException異常。這并不是因為HashTable有什么特殊的實現(xiàn)層面的原因?qū)е虏荒苤С謓ull鍵和null值钞它,這僅僅是因為HashMap在實現(xiàn)時對null做了特殊處理拜银,將null的hashCode值定為了0,從而將其存放在哈希表的第0個bucket中遭垛。

  • 容量大小及擴(kuò)容方式不同
    HashMap和HashTable都使用哈希表來存儲鍵值對尼桶。在數(shù)據(jù)結(jié)構(gòu)上是基本相同的,都創(chuàng)建了一個繼承自Map.Entry的私有的內(nèi)部類Entry锯仪,每一個Entry對象表示存儲在哈希表中的一個鍵值對泵督。
    Entry對象唯一表示一個鍵值對,有四個屬性:
    -K key 鍵對象
    -V value 值對象
    -int hash 鍵對象的hash值
    -Entry entry 指向鏈表中下一個Entry對象庶喜,可為null小腊,表示當(dāng)前Entry對象在鏈表尾部救鲤。
    HashMap/HashTable內(nèi)部用Entry數(shù)組實現(xiàn)哈希表,而對于映射到同一個哈希桶(數(shù)組的同一個位置)的鍵值對秩冈,使用Entry鏈表來存儲本缠。
    HashMap/HashTable初始容量大小和每次擴(kuò)充容量大小的不同:HashTable默認(rèn)的初始大小為11,之后每次擴(kuò)充為原來的2n+1入问。HashMap默認(rèn)的初始化大小為16丹锹,之后每次擴(kuò)充為原來的2倍。如果在創(chuàng)建時給定了初始化大小芬失,那么HashTable會直接使用你給定的大小楣黍,而HashMap會將其擴(kuò)充為2的冪次方大小。

  • 線程安全性不同
    HashTable是同步的(原因:公開的方法比如get都使用了synchronized描述符麸折。而遍歷視圖比如keySet都使用了Collections.synchronizedXXX進(jìn)行了同步包裝)锡凝,HashMap不是,也就是說HashTable在多線程使用的情況下垢啼,不需要做額外的同步窜锯,而HashMap則不行。
    由于Hashtable是線程安全的也是synchronized芭析,所以在單線程環(huán)境下它比HashMap速度要慢锚扎。
    如果要保持線程安全可以選用ConcurrentHashMap,ConcurrentHashMap引入了分割(segmentation)馁启,不論它變得多么大驾孔,僅僅需要鎖定map的某個部分,而其它的線程不需要等到迭代完成才能訪問map惯疙。Hashtable則會鎖定整個map翠勉,Hashtable的大小增加到一定的時候,性能會急劇下降霉颠,因為迭代時需要被鎖定很長的時間对碌。簡而言之,在迭代的過程中蒿偎,ConcurrentHashMap僅僅鎖定map的某個部分朽们,而Hashtable則會鎖定整個map,ConcurrentHashMap比Hashtable高效诉位。

  • Hash值不同
    Hashtable計算hash值骑脱,直接用key的hashCode(),而HashMap重新計算了key的hash值苍糠,Hashtable在求hash值對應(yīng)的位置索引時叁丧,用取模運(yùn)算,而HashMap在求位置索引時,則用與運(yùn)算拥娄,且這里一般先用hash&0x7FFFFFFF后坷衍,再對length取模,&0x7FFFFFFF的目的是為了將負(fù)的hash值轉(zhuǎn)化為正值条舔,因為hash值有可能為負(fù)數(shù)枫耳,而&0x7FFFFFFF后,只有符號位改變孟抗,而后面的位都不變迁杨。

  • 迭代器不同
    HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的凄硼。所以當(dāng)有其它線程改變了HashMap的結(jié)構(gòu)(增加或者移除元素)铅协,將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常摊沉。但這并不是一個一定發(fā)生的行為狐史,要看JVM。這條同樣也是Enumeration和Iterator的區(qū)別说墨。

由于Hashtable是線程安全的也是synchronized骏全,所以在單線程環(huán)境下它比HashMap要慢。如果你不需要同步尼斧,只需要單一線程姜贡,那么使用HashMap性能要好過Hashtable。
HashMap不能保證隨著時間的推移Map中的元素次序是不變的棺棵。

要注意的一些重要術(shù)語:

  • sychronized意味著在一次僅有一個線程能夠更改Hashtable楼咳。就是說任何線程要更新Hashtable時要首先獲得同步鎖,其它線程要等到同步鎖被釋放之后才能再次獲得同步鎖更新Hashtable烛恤。

  • Fail-safe和iterator迭代器相關(guān)母怜。如果某個集合對象創(chuàng)建了Iterator或者ListIterator,然后其它的線程試圖“結(jié)構(gòu)上”更改集合對象缚柏,將會拋出ConcurrentModificationException異常苹熏。但其它線程可以通過set()方法更改集合對象是允許的,因為這并沒有從“結(jié)構(gòu)上”更改集合船惨。但是假如已經(jīng)從結(jié)構(gòu)上進(jìn)行了更改柜裸,再調(diào)用set()方法缕陕,將會拋出IllegalArgumentException異常粱锐。

  • 結(jié)構(gòu)上的更改指的是刪除或者插入一個元素,這樣會影響到map的結(jié)構(gòu)扛邑。

Fail-fast與Fail-safe比較

什么是 fail-fast 機(jī)制?

fail-fast機(jī)制在遍歷一個集合時怜浅,當(dāng)集合結(jié)構(gòu)被修改,會拋出Concurrent Modification Exception。

fail-fast會在以下兩種情況下拋出ConcurrentModificationException

  • 單線程環(huán)境

集合被創(chuàng)建后恶座,在遍歷它的過程中修改了結(jié)構(gòu)搀暑。

注意 remove()方法會讓expectModcount和modcount 相等,所以是不會拋出這個異常跨琳。

  • 多線程環(huán)境

當(dāng)一個線程在遍歷這個集合自点,而另一個線程對這個集合的結(jié)構(gòu)進(jìn)行了修改。

注意脉让,迭代器的快速失敗行為無法得到保證桂敛,因為一般來說,不可能對是否出現(xiàn)不同步并發(fā)修改做出任何硬性保證溅潜∈趸#快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException。因此滚澜,為提高這類迭代器的正確性而編寫一個依賴于此異常的程序是錯誤的做法:迭代器的快速失敗行為應(yīng)該僅用于檢測 bug粗仓。

fail-fast機(jī)制是如何檢測的?

迭代器在遍歷過程中是直接訪問內(nèi)部數(shù)據(jù)的设捐,因此內(nèi)部的數(shù)據(jù)在遍歷的過程中無法被修改借浊。為了保證不被修改,迭代器內(nèi)部維護(hù)了一個標(biāo)記 “mode” 萝招,當(dāng)集合結(jié)構(gòu)改變(添加刪除或者修改)巴碗,標(biāo)記"mode"會被修改,而迭代器每次的hasNext()和next()方法都會檢查該"mode"是否被改變即寒,當(dāng)檢測到被修改時橡淆,拋出Concurrent Modification Exception。
下面看看ArrayList迭代器部分的源碼母赵。

private class Itr implements Iterator<E> {  
        int cursor;  
        int lastRet = -1;  
        int expectedModCount = ArrayList.this.modCount;  
  
        public boolean hasNext() {  
            return (this.cursor != ArrayList.this.size);  
        }  
  
        public E next() {  
            checkForComodification();  
            /** 省略此處代碼 */  
        }  
  
        public void remove() {  
            if (this.lastRet < 0)  
                throw new IllegalStateException();  
            checkForComodification();  
            /** 省略此處代碼 */  
        }  
  
        final void checkForComodification() {  
            if (ArrayList.this.modCount == this.expectedModCount)  
                return;  
            throw new ConcurrentModificationException();  
        }  
    }  

可以看到它的標(biāo)記“mode”為 expectedModeCount逸爵。

fail-safe機(jī)制

fail-safe任何對集合結(jié)構(gòu)的修改都會在一個復(fù)制的集合上進(jìn)行修改,因此不會拋出ConcurrentModificationException凹嘲。

fail-safe機(jī)制有兩個問題

  • 需要復(fù)制集合师倔,產(chǎn)生大量的無效對象,開銷大周蹭。

  • 無法保證讀取的數(shù)據(jù)是目前原始數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)趋艘。

fail-fast和 fail-safe 的區(qū)別

區(qū)別 Fail Fast Iterator Fail Safe Iterator Enumeration
Throw ConcurrentModification Exception Yes No No
Clone object No Yes Yes
Memory Overhead No Yes Yes
Examples HashMap,Vector,ArrayList,HashSet CopyOnWriteArrayList,ConcurrentHashMap Vector,Hashtable

Iterator和 Enumeration 的區(qū)別

  • 函數(shù)接口不同
    Enumeration只有2個函數(shù)接口。通過Enumeration凶朗,我們只能讀取集合的數(shù)據(jù)瓷胧,而不能對數(shù)據(jù)進(jìn)行修改。
    Iterator只有3個函數(shù)接口棚愤。Iterator除了能讀取集合的數(shù)據(jù)之外搓萧,也能數(shù)據(jù)進(jìn)行刪除操作杂数。
  • Iterator支持fail-fast機(jī)制,而Enumeration不支持瘸洛。
    Enumeration 是JDK 1.0添加的接口揍移。使用到它的函數(shù)包括Vector、Hashtable等類反肋,這些類都是JDK 1.0中加入的那伐,Enumeration存在的目的就是為它們提供遍歷接口。Enumeration本身并沒有支持同步石蔗,而在Vector喧锦、Hashtable實現(xiàn)Enumeration時,添加了同步抓督。
    而Iterator 是JDK 1.2才添加的接口燃少,它也是為了HashMap、ArrayList等集合提供遍歷接口铃在。Iterator是支持fail-fast機(jī)制的:當(dāng)多個線程對同一個集合的內(nèi)容進(jìn)行操作時阵具,就可能會產(chǎn)生fail-fast事件。

我們能否讓HashMap同步定铜?

HashMap可以通過下面的語句進(jìn)行同步:
Map m = Collections.synchronizeMap(hashMap);

結(jié)論

Hashtable和HashMap有幾個主要的不同:線程安全以及速度阳液。僅在你需要完全的線程安全的時候使用Hashtable,而如果你使用Java 5或以上的話揣炕,請使用ConcurrentHashMap吧帘皿。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市畸陡,隨后出現(xiàn)的幾起案子鹰溜,更是在濱河造成了極大的恐慌,老刑警劉巖丁恭,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曹动,死亡現(xiàn)場離奇詭異,居然都是意外死亡牲览,警方通過查閱死者的電腦和手機(jī)墓陈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來第献,“玉大人贡必,你說我怎么就攤上這事∮购粒” “怎么了仔拟?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長岔绸。 經(jīng)常有香客問我理逊,道長,這世上最難降的妖魔是什么盒揉? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任晋被,我火速辦了婚禮,結(jié)果婚禮上刚盈,老公的妹妹穿的比我還像新娘羡洛。我一直安慰自己,他們只是感情好藕漱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布欲侮。 她就那樣靜靜地躺著,像睡著了一般肋联。 火紅的嫁衣襯著肌膚如雪威蕉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天橄仍,我揣著相機(jī)與錄音韧涨,去河邊找鬼。 笑死侮繁,一個胖子當(dāng)著我的面吹牛虑粥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宪哩,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼娩贷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锁孟?” 一聲冷哼從身側(cè)響起彬祖,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎品抽,沒想到半個月后涧至,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桑包,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年南蓬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哑了。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡赘方,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弱左,到底是詐尸還是另有隱情窄陡,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布拆火,位于F島的核電站跳夭,受9級特大地震影響涂圆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜币叹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一润歉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颈抚,春花似錦踩衩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匹舞,卻和暖如春褐鸥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赐稽。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工晶疼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人又憨。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓翠霍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蠢莺。 傳聞我的和親對象是個殘疾皇子寒匙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344