Java基礎面試——各種集合

前言

Java現(xiàn)在的市場占用率有目共睹愚隧,為(ying)了(fu)學(mian)好(shi)Java海蔽,Java的一些底層知識不得不了解锥咸,與其需要的時候東一錘撼嗓,西一刀到處找柬采,這里總結(jié)一下,以備不時之需且警。相關圖片和詳細資料都可以在參考文章下的源文章中找到粉捻。

HashMap

HashMap.png

HashMap是數(shù)組和鏈表的組合。HashMap的高性能依賴數(shù)組斑芜,鏈表是為了處理哈希沖突肩刃。HashMap通過對Key的hashCode()進行哈希擾亂(hash()),進一步與數(shù)組lenght-1進行&運算,最終通過indexFor()決定具體的地址盈包。

常規(guī)狀態(tài)下沸呐,HashMap的尋址時間復雜度為O(1)⌒铮可是哈希函數(shù)不能保證計算出來的地址是唯一的垂谢,也就是說,會存在多個不同的Key疮茄,指向同一個地址滥朱,這被成為哈希沖突(碰撞)。這時通過鏈表將指向同位置的數(shù)據(jù)鏈接力试,對于這個位置的數(shù)據(jù)徙邻,通過Key的equals()進行匹配,尋址時間復雜度可以認為為O(n)畸裳,n為該位置鏈表長度缰犁。所以,HashMap中鏈表越少越短怖糊,其性能越高帅容。

HashMap存在容量(Capacity,默認16)與負載因子(loadFactor伍伤,默認0.75)并徘,容量指數(shù)組的長度,也是HashMap中允許存放的數(shù)據(jù)量扰魂,負載因子指數(shù)據(jù)占比麦乞。負載因子的存在是為了減少哈希沖突,我們可以理解為劝评,數(shù)據(jù)量越接近最大負載姐直,越容易發(fā)生哈希碰撞。當數(shù)據(jù)占位比達到負載上限蒋畜,需要進行擴容声畏。每次擴容即將當前容量翻倍,這樣可以保證數(shù)據(jù)在搬運到數(shù)組中時姻成,hash與length-1進行與運算出來的下標保持不變插龄。所以,HashMap的容量永遠是2的指數(shù)冪佣渴。

線程不安全,在JDK1.7中初斑,多線程同時擴容時辛润,執(zhí)行到resize中的transfer方法重新散列table,散列元素為從頭插入(頭插法),最終形成環(huán)形鏈表砂竖,下一次調(diào)用get方法或put方法遍歷這個Hash數(shù)組的位置的鏈表真椿,如果元素key不存在,就在這個循環(huán)鏈表中無限循環(huán)遍歷了乎澄,因為不存在尾結(jié)點的指針域為null停止循環(huán)遍歷了突硝。在JDK1.8中,通過尾插法置济,在resize時保持原來鏈表元素的順序解恰,避免循環(huán)鏈表的bug。HashMap線程不安全的問題還有很多浙于,比如多線程resize時护盈,put會出現(xiàn)原來數(shù)據(jù)節(jié)點的丟失;一個線程通過迭代器遍歷或刪除時羞酗,另一個線程修改了HashMap腐宋,則modCount++,造成迭代器中的expectedModCount與HashMap中的modCount不一樣檀轨,拋出ConcurrentModificationException異常等等原因胸竞。所以,HashMap就是設計給單線程作業(yè)的参萄,多線程要么二次通過并發(fā)封裝卫枝,要么使用HashTable或ConCurrentHashMap等線程安全的集合。

JDK1.8優(yōu)化HashMap拧揽,轉(zhuǎn)鏈表為紅黑樹剃盾。無論什么樣的哈希函數(shù),都無法避免哈希碰撞的問題淤袜,為此JDK1.8在JDK1.7的基礎上對原來的鏈表結(jié)構(gòu)進行了優(yōu)化痒谴。當鏈表長度超過8時,將鏈表轉(zhuǎn)化為紅黑樹铡羡,紅黑樹的時間復雜度為O(lgn)积蔚,可以有效提升HashMap的查找性能。

HashMap put方法邏輯圖(JDK1.8).png

從以上過程可以看出烦周,HashMap本質(zhì)上是亂序的尽爆,雖然我們在Java中遍歷時,Key的排列看似有序的读慎,但不一定穩(wěn)定漱贱。

HashMap知識點

  • 數(shù)組鏈表組合
  • 容量總是2的指數(shù)冪夭委;
  • 紅黑樹優(yōu)化幅狮;
  • 非線程安全
  • 無序

note*:哈希沖突解決方案,開放定址法(發(fā)生沖突崇摄,繼續(xù)尋找下一塊未被占用的存儲地址)擎值、再散列函數(shù)法、鏈地址法(數(shù)組+鏈表)等逐抑。

TreeMap

TreeMap基于紅黑樹實現(xiàn)鸠儿,所以其增查改刪的復雜度也是O(lgn)。雖然比起HashMap來說厕氨,其性能有所缺憾进每,但是,在類結(jié)構(gòu)上腐巢,TreeMap實現(xiàn)了SortedMap接口品追,允許通過默認或者自定義的Comparator進行排序,實現(xiàn)有序的Map冯丙。默認的順序與常規(guī)的List不同肉瓦,不是添加順序,而是通過Key的排列順序排序的胃惜。TreeMap中也沒有對線程安全做相關的處理泞莉,也會出現(xiàn)數(shù)據(jù)丟失、數(shù)據(jù)同步異常等問題船殉,所以TreeMap也是線程不安全的鲫趁。

TreeMap知識點

  • 紅黑樹
  • 非線程安全利虫;
  • 有序

HashTable

HashTable是比較老的Hash集合挨厚,現(xiàn)在基本不用了。基本實現(xiàn)與HashMap相同糠惫,由于保障線程安全的原因疫剃,為一些特別的方法加上了synchronized(函數(shù)級synchronized),在多進程的情況下硼讽,同時只允許一個進程訪問該方法巢价,所以性能上稍差。由于synchronized的原因固阁,HashTable的操作在多線程下會變成串行壤躲,數(shù)量多了就會造成阻塞,因此备燃,由于其是直接在方法上加入synchronized控制并發(fā)碉克,所以阻塞的概率還是比較大的。但是并齐,因為這種大段串行競爭鎖的機制漏麦,每次都是鎖住整個表法瑟,HashMap能反映最近的數(shù)據(jù)更新,更保險唁奢。當前,一般是通過HashMap并發(fā)包裝窝剖,或者ConcurrentHashMap在多線程下保證線程安全麻掸。
HashTable知識點

  • 線程安全
  • 串行赐纱;
  • 阻塞脊奋;

ConcurrentHashMap

ConcurrentHashMap是為了兼顧性能和線程安全而存在的。ConcurrentHashMap也有HashMap疙描,因為在數(shù)據(jù)處理上使用了相同的方法诚隙,數(shù)組-鏈表-[紅黑樹],所以保證了效率起胰。但是它又是線程安全的久又,因為它在處理的通過synchronized來保障數(shù)據(jù)同步。但是它的效率又高于HashTable效五,因為使用了分段鎖技術地消,對HashMap的數(shù)據(jù)單元Node的數(shù)據(jù)結(jié)構(gòu)進行了調(diào)整,并將鎖細粒度化畏妖,從而實線分段鎖脉执,提升了線程安全下的性能。

JDK1.7中戒劫,通過Segment實現(xiàn)分段鎖半夷。ConcurrentHashMap由多個Segment組成,每個Segment下維護一個HashEntry數(shù)組迅细,這個數(shù)組的結(jié)構(gòu)就和HashMap一樣了巫橄,每次操作時,鎖住數(shù)據(jù)對應的Segment疯攒,實現(xiàn)數(shù)據(jù)安全嗦随,比起HashTable鎖住整個表,效率提升了很多敬尺。

JDK1.8中枚尼,通過CAS+Synchronized來保證并發(fā)更新的安全。CAS是樂觀鎖的一種實現(xiàn)砂吞,讀取無所謂署恍,在數(shù)據(jù)更新時,通過compareAndSwap蜻直,保障數(shù)據(jù)安全盯质。1.8中舍棄了Segment袁串,數(shù)據(jù)結(jié)構(gòu)與1.8的HashMap差不多,將鎖的粒度從之前的Segment呼巷,細粒度到Node囱修。如果當前操作的Node為NULL,CAS保證原子性王悍,非空破镰,synchronized上鎖當前位置鏈表頭或紅黑樹根節(jié)點,這樣的結(jié)合方式可以有效提升并發(fā)操作效率压储。

ConcurrentHashMap的數(shù)據(jù)的弱一致鲜漩。ConcurrentHashMap通過volatile保證Node數(shù)組對多線程的可見性,這樣get時集惋,就總能得到真實的數(shù)據(jù)孕似。當?shù)鱥terator創(chuàng)建后,數(shù)據(jù)再發(fā)生改變刮刑,不會將操作直接作用到原始數(shù)據(jù)上喉祭,而是new新的數(shù)據(jù),當iterator完成后再將修改作用到原始數(shù)據(jù)上雷绢,所以CocurrentHashMap不能及時的反應數(shù)據(jù)的更新臂拓。這保證了多個線程并發(fā)執(zhí)行的連續(xù)性和擴展性,是性能提升的關鍵习寸,是一致性與效率之間的一種權(quán)衡胶惰。

ConcurrentHashMap知識點

  • 線程安全
  • 分段鎖霞溪;
  • CAS+Synchronized

參考文章

深入淺出學Java——HashMap
java遍歷TreeMap元素順序不是添加的順序問題
JAVA學習-紅黑樹詳解
JDK1.8 HashMap和TreeMap區(qū)別,讀懂這一篇就夠了
紅黑樹時間復雜度證明(O(lgn))
Java 集合系列12之 TreeMap詳細介紹(源碼解析)和使用示例
HashMap 和 HashTable 區(qū)別
Java基礎之ConcurrentHashMap
JAVA7與JAVA8中的HASHMAP和CONCURRENTHASHMAP知識點總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孵滞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鸯匹,更是在濱河造成了極大的恐慌坊饶,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殴蓬,死亡現(xiàn)場離奇詭異匿级,居然都是意外死亡,警方通過查閱死者的電腦和手機染厅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門痘绎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肖粮,你說我怎么就攤上這事孤页。” “怎么了涩馆?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵行施,是天一觀的道長允坚。 經(jīng)常有香客問我,道長蛾号,這世上最難降的妖魔是什么稠项? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮鲜结,結(jié)果婚禮上皿渗,老公的妹妹穿的比我還像新娘。我一直安慰自己轻腺,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布划乖。 她就那樣靜靜地躺著贬养,像睡著了一般。 火紅的嫁衣襯著肌膚如雪琴庵。 梳的紋絲不亂的頭發(fā)上误算,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音迷殿,去河邊找鬼儿礼。 笑死,一個胖子當著我的面吹牛庆寺,可吹牛的內(nèi)容都是我干的蚊夫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼懦尝,長吁一口氣:“原來是場噩夢啊……” “哼知纷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起陵霉,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤琅轧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后踊挠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乍桂,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年效床,在試婚紗的時候發(fā)現(xiàn)自己被綠了睹酌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡剩檀,死狀恐怖忍疾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谨朝,我是刑警寧澤卤妒,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布甥绿,位于F島的核電站,受9級特大地震影響则披,放射性物質(zhì)發(fā)生泄漏共缕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一士复、第九天 我趴在偏房一處隱蔽的房頂上張望图谷。 院中可真熱鬧,春花似錦阱洪、人聲如沸便贵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽承璃。三九已至,卻和暖如春蚌本,著一層夾襖步出監(jiān)牢的瞬間盔粹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工程癌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舷嗡,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓嵌莉,卻偏偏與公主長得像进萄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子锐峭,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348