Java 集合框架中的老炮與新秀:HashTable 和 HashMap 誰(shuí)更勝一籌?
引言
嗨,大家好呀,我是你們的技術(shù)伙伴小米!
前幾天有個(gè)老同學(xué)在微信上找我吐槽:“小米啊,這次面試被問(wèn)到 HashMap 和 HashTable 的區(qū)別,明明知道兩個(gè)都是存鍵值對(duì)的,愣是沒(méi)講清楚,氣死我了!”
聽(tīng)完這話,我忍不住笑著打趣:“你這是‘一問(wèn) Hash,智商掉’系列吧!”當(dāng)然,也順便給他科普了一波 HashMap 和 HashTable 的區(qū)別。想想這個(gè)問(wèn)題還挺經(jīng)典的,今天咱們就通過(guò)講故事的方式來(lái)聊一聊,順便幫更多小伙伴搞懂它們的差異。
故事背景:HashMap 和 HashTable 的江湖初相遇
在 Java 的江湖中,HashMap 和 HashTable 是一對(duì)“性格迥異的兄弟”。雖然名字相似、功能上都用來(lái)存儲(chǔ)鍵值對(duì),但他們卻有著本質(zhì)的不同。接下來(lái),我們通過(guò)“武林大會(huì)”的視角,一步步解析這兩位高手的招式與性格吧!
1.誰(shuí)更年輕?誰(shuí)的衣缽更新潮?
HashMap 是 Java 1.2 引入的,是江湖中的“新生代選手”,它屬于 Java Collections Framework 的一部分,設(shè)計(jì)上追求高效與靈活性。
HashTable 則可以追溯到 Java 1.0,作為江湖中的“老古董”,它出道早,但隨著時(shí)代的發(fā)展,逐漸被認(rèn)為有些“古板”。
差異總結(jié):
- HashMap: 引入于 JDK 1.2,屬于 Collections Framework。
- HashTable: 出現(xiàn)在 JDK 1.0,較為古老。
2.是否線程安全?誰(shuí)更適合多線程場(chǎng)景?
如果說(shuō)江湖中有什么大風(fēng)大浪,那就是多線程的挑戰(zhàn)了。在這個(gè)問(wèn)題上,HashMap 和 HashTable 的表現(xiàn)截然不同。
- HashMap: 是非線程安全的,多個(gè)線程同時(shí)操作 HashMap 可能導(dǎo)致數(shù)據(jù)不一致。如果要在多線程環(huán)境中使用,需要手動(dòng)加鎖,或者用 Collections.synchronizedMap 方法包裝成線程安全的版本。
- HashTable: 天生是線程安全的,因?yàn)樗姆椒ㄊ褂昧?synchronized 關(guān)鍵字加鎖。然而,這種全局鎖的機(jī)制在高并發(fā)環(huán)境中會(huì)帶來(lái)性能瓶頸。
差異總結(jié):
- HashMap: 非線程安全,適合單線程場(chǎng)景或需手動(dòng)處理線程安全。
- HashTable: 線程安全,但性能不佳。
3.是否允許 null 值?處理空值有何不同?
有一次,江湖中某位俠客提出了一個(gè)刁鉆問(wèn)題:能不能存放 null 鍵或者 null 值??jī)尚值艿幕卮鹩植灰粯樱?/p>
- HashMap: 可以存儲(chǔ)一個(gè) null 鍵和多個(gè) null 值。
- HashTable: 不允許任何 null 鍵或 null 值,原因是它的設(shè)計(jì)中沒(méi)有處理 null 的邏輯,會(huì)直接拋出 NullPointerException。
差異總結(jié):
- HashMap: 支持一個(gè) null 鍵和多個(gè) null 值。
- HashTable: 不支持 null 鍵和 null 值。
4.性能對(duì)比與背后設(shè)計(jì)
HashMap 在性能上優(yōu)于 HashTable,這是因?yàn)椋?/p>
- 鎖機(jī)制的差異:HashMap 不涉及同步,所以性能更高。而 HashTable 的全局鎖導(dǎo)致性能較差。數(shù)據(jù)結(jié)構(gòu)的優(yōu)化:JDK 1.8 之后,HashMap 引入了紅黑樹(shù),當(dāng)鏈表長(zhǎng)度超過(guò)一定閾值時(shí),會(huì)將鏈表轉(zhuǎn)換為紅黑樹(shù),降低查找時(shí)間復(fù)雜度。而 HashTable 仍然使用傳統(tǒng)鏈表結(jié)構(gòu)。
差異總結(jié):
- HashMap: 更高效,支持紅黑樹(shù)優(yōu)化。
- HashTable: 全局鎖限制性能,無(wú)紅黑樹(shù)優(yōu)化。
5.迭代器的行為是否一致?
在江湖中,有時(shí)我們需要遍歷這些鍵值對(duì)。兩兄弟在這方面也有不同的性格:
- HashMap: 使用 fail-fast 機(jī)制,當(dāng)一個(gè)線程遍歷時(shí),如果其他線程對(duì)結(jié)構(gòu)進(jìn)行了修改,會(huì)拋出 ConcurrentModificationException。
- HashTable: 使用的是 Enumerator,功能類似但較老舊,沒(méi)有 fail-fast 機(jī)制。
差異總結(jié):
- HashMap: 使用 fail-fast 的 Iterator。
- HashTable: 使用老舊的 Enumerator,無(wú) fail-fast。
總結(jié)大比拼:誰(shuí)更適合你的項(xiàng)目?
在選擇上,其實(shí)很簡(jiǎn)單:
- 如果你的項(xiàng)目運(yùn)行在單線程環(huán)境中,或使用現(xiàn)代工具解決線程安全問(wèn)題(如 ConcurrentHashMap),那么果斷選 HashMap!
- 如果你正在維護(hù)一個(gè)老項(xiàng)目,而這個(gè)項(xiàng)目已經(jīng)在使用 HashTable,那就維持現(xiàn)狀,除非有特別理由優(yōu)化性能。
額外彩蛋:為什么不直接用 ConcurrentHashMap?
如果你需要線程安全的方案,那 ConcurrentHashMap 是一個(gè)更優(yōu)雅的選擇!它在分段鎖的基礎(chǔ)上實(shí)現(xiàn)了高并發(fā)性能,并且大部分場(chǎng)景中都可以無(wú)縫替代 HashTable。