今天看代碼,想到去年發生的HashMap發生的CPU使用率100%的事件,轉載下當時看的三個比較不錯的博客(很是推薦)html
參考:http://coolshell.cn/articles/9606.htmljava
http://github.thinkingbar.com/hashmap-analysis/git
http://developer.51cto.com/art/201102/246431.htm程序員
人物:github
王小胖:性別:男。程序員,工做經驗1 year。愛好:吃肉、電玩、馬小花。特技:吃肉不用考慮胃的容量。算法
馬小花:性別:女。學生,工做經驗0 year。愛好:蛋糕、臭美、王小胖。特技:可以降服王小胖……shell
/**2011年2月,電影《將愛情進行到底》火得不得了。週末,小胖也陪着小花去看這部電影。放映中,小花被影片中的靖哥哥和杜拉拉感動的一沓糊塗,而小胖則內心暗自後悔沒有買一袋大爆米花來打發這無聊的時間。影片結束,小花已是鼻涕一把淚一把,小胖也只有裝模做樣地抽動了幾下鼻子,一心只想着一下子是吃麥當勞仍是必勝客。*/數組
回到家中,小胖和小花各自玩着電腦。多線程
小花:胖子,你知道Hashtable和HashMap的區別嗎?併發
小胖:略知。
小花:……裝什麼!!給我講講!!!
小胖:好的……
第一個區別就先來講說繼承關係吧。
若是你在baidu裏google一下(技術類文章的搜索仍是推薦google),會發現網上的大體說法與「因爲Java發展的歷史緣由。Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現。」相同。這種說法沒有錯,可是胖子以爲不夠準確,特別是對於咱們這種大衆菜鳥來講,若是不去深究的話,可能就會形成一些理解上的差別。簡單的認爲Hashtable沒有繼承Map接口。胖子以前就犯過這樣的錯誤(胖子認可本身笨,是真笨……)。
小花:那你怎麼知道它們兩個各自的繼承關係呢?胖子。
咱們能夠參考一下最新的JDK1.6的源碼,看看這兩個類的定義:
Java代碼
能夠看到hashtable也是繼承了Map接口。它們的不一樣是Hashtable(since JDK1.0)就繼承了Dictionary這個抽象類,而HashMap(since JDK1.2)繼承的則是AbstractMap這個抽象類。由於在Hashtable中看到繼承Map後所實現的方法是JDK1.2版本時加上去的,因此胖子猜測多是在JDK 1.2開發時Sun工程師出於統一的考慮使得Hashtable也繼承了Map接口。
小花:哦,原來JDK源碼還能看出來這個。
小胖:……後面還能看出更多東西的。
小花:好期待啊。
第二個區別咱們從同步和併發性上來講說它們兩個的不一樣。
能夠經過這兩個類得源碼來分析,Hashtable中的主要方法都作了同步處理,而HashMap則沒有。能夠說Hashtable在默認狀況支持同步,而HashMap在默認狀況下是不支持的。咱們在多線程併發的環境下,能夠直接使用Hashtable,可是要使用HashMap的話就要本身增長同步處理了。對HashMap的同步處理可使用Collections類提供的synchronizedMap靜態方法;或者直接使用JDK5.0以後提供的java.util.concurrent包裏的ConcurrentHashMap類。
小胖:synchronizedMap靜態方法和ConcurrentHashMap類我會之後再給你詳細講一下的。肥婆。
小花:你保證啊。鑰匙忘了你知道後果的。
小胖:好的……
第三個區別就是它們對於null值的處理方式了。
咱們依然可以從源代碼中得知,Hashtable中,key和value都不容許出現null值。
Java代碼
在咱們使用上面的方法時,如參數value爲null,能夠從代碼中直接看出程序會拋出NullPointerException;而在key爲null時,則會在「int hash = key.hashCode();「這段計算Hash值的過程當中拋出NullPointerException。而在在HashMap中,容許null做爲key存在,而且和其餘key的特性同樣,這樣的null值key只能有一個;另外HashMap容許多個value爲null。這樣你們就要注意了, HashMap中就不能用get(key)方法來判斷HashMap中是否存在某個key,由於value爲null和不存在該key的Entry都會返回null值,而應該用containsKey()方法來判斷了。(詳見源碼剖析)
Java代碼
結果:
Java代碼
HashMap對於null值key的處理網上有說「null 用new Object()來代替,其Entry.hashCode=0,並且在取出的時候還會還回null的。」胖子我在讀取源碼的過程當中看到了null值的hash值確實是0 (內部實現的數組中的index也是),可是能力有限沒有看到轉爲new Object()的過程。
小花: 原來hashMap的containsKey還有這麼個陷阱,之後肥婆要當心了。
第四個不一樣就是它們兩個Hash值的獲取方式了。
仍是經過源代碼源代碼,Hashtable是直接使用key對象的hash值。
Java代碼
而HashMap則是利用key對象的hash值從新計算一個新的hash值。
Java代碼
小花:胖子,都用了hash算法,你給我講講Hash算法吧。
小胖:嗯……之後的,今天我比較忙(實際上是不會)。
小花:你是否是不會啊?嘿嘿(壞笑)。
小胖:什麼不會……談下一話題……
第五個不一樣就是Hashtable和HashMap它們兩個內部實現方式的數組的初始大小和擴容的方式。
HashMap中內部數組的初始容量是16, 加載因子爲0.75,並且數組容量增容後也要是2指數次冪:
Java代碼
HashTable中的內部數組的初始容量是11,加載因子也是0.75數組的增容方式爲(oldCapacity * 2 + 1):
Java代碼
第六個不一樣咱們從它們兩個遍歷方式的內部實現上來講。
Hashtable HashMap都使用了 Iterator。而因爲歷史緣由,Hashtable還使用了Enumeration的方式 。
小花:Iterator和Enumeration的區別是什麼啊?給我講講。
小胖:我不是說我沒有時間嘛,下回的。
小花:我都記下來,免得你給我混過去。(拿起筆開始記帳中)
小胖:……(緊張)
第七個不一樣時它們的拷貝構造函數的不一樣。
依然是經過查看源碼,能夠發現它們兩個對於拷貝函數初始容量的不一樣值。
HashMap的實現是:
Java代碼
而Hashtable的實現是:
Java代碼
小胖:今天講的已經不少了。我有點餓了,肥婆。
小花:看你今天的表現這麼好。走,帶你去吃烤肉去。
小胖:哈哈,肥婆萬歲。