【面試系列】Map集合

小茵:今天來講講Map吧限番,你對Map了解多少姻乓?

小奧:Map在Java里邊是一個(gè)接口蕉拢,常見的實(shí)現(xiàn)類有HashMap、LinkedHashMap刻两、TreeMap和ConcurrentHashMap

小奧:在Java里邊增蹭,哈希表的結(jié)構(gòu)是數(shù)組+鏈表的方式。

小奧:HashMap底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表/紅黑樹

小奧:LinkedHashMap底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表/紅黑樹+雙向鏈表

小奧:TreeMap底層數(shù)據(jù)結(jié)構(gòu)是紅黑樹

小奧:而ConcurrentHashMap底層數(shù)據(jù)結(jié)構(gòu)也是數(shù)組+鏈表/紅黑樹

image.png

小茵:我們先以HashMap開始吧磅摹,你能講講當(dāng)你new一個(gè)HashMap的時(shí)候沪铭,會(huì)發(fā)生什么嗎?

小奧:HashMap有幾個(gè)構(gòu)造方法偏瓤,但最主要的就是指定初始值大小和負(fù)載因子的大小杀怠。

小奧:如果我們不指定,默認(rèn)HashMap的大小為16厅克,負(fù)載因子的大小為0.75

小奧:HashMap的大小只能是2次冪的赔退,假設(shè)你傳一個(gè)10進(jìn)去,實(shí)際上最終HashMap的大小是16证舟,你傳一個(gè)7進(jìn)去硕旗,HashMap最終的大小是8,具體的實(shí)現(xiàn)在tableSizeFor可以看到女责。

小奧:我們把元素放進(jìn)HashMap的時(shí)候漆枚,需要算出這個(gè)元素所在的位置(hash)。

小奧:在HashMap里用的是位運(yùn)算來代替取模抵知,能夠更加高效地算出該元素所在的位置墙基。

小奧:為什么HashMap的大小只能是2次冪,因?yàn)橹挥写笮?次冪時(shí)刷喜,才能合理用位運(yùn)算替代取模残制。

image.png

小奧:而負(fù)載因子的大小決定著哈希表的擴(kuò)容和哈希沖突。

小奧:比如現(xiàn)在我默認(rèn)的HashMap大小為16掖疮,負(fù)載因子為0.75初茶,這意味著數(shù)組最多只能放12個(gè)元素,一旦超過12個(gè)元素浊闪,則哈希表需要擴(kuò)容恼布。

小奧:怎么算出是12呢?很簡單搁宾,就是16*0.75折汞。每次put元素進(jìn)去的時(shí)候,都會(huì)檢查HashMap的大小有沒有超過這個(gè)閾值猛铅,如果有字支,則需要擴(kuò)容。

image.png

小奧:鑒于上面的說法(HashMap的大小只能是2次冪),所以擴(kuò)容的時(shí)候時(shí)候默認(rèn)是擴(kuò)原來的2倍

小奧:擴(kuò)容這個(gè)操作肯定是耗時(shí)的堕伪,那能不能把負(fù)載因子調(diào)高一點(diǎn)揖庄,比如我要調(diào)至為1,那我的HashMap就等到16個(gè)元素的時(shí)候才擴(kuò)容呢欠雌。

小奧:是可以的蹄梢,但是不推薦。負(fù)載因子調(diào)高了富俄,這意味著哈希沖突的概率會(huì)增高禁炒,哈希沖突概率增高,同樣會(huì)耗時(shí)(因?yàn)椴檎业乃俣茸兟耍?/p>

小茵:那我想問下霍比,在put元素的時(shí)候幕袱,傳遞的Key是怎么算哈希值的?

小奧:實(shí)現(xiàn)就在hash方法上悠瞬,可以發(fā)現(xiàn)的是们豌,它是先算出正常的哈希值,然后與高16位做異或運(yùn)算浅妆,產(chǎn)生最終的哈希值望迎。

小奧:這樣做的好處可以增加了隨機(jī)性,減少了碰撞沖突的可能性凌外。

image.png
image.png

小茵:了解辩尊,你簡單再說下put和get方法的實(shí)現(xiàn)吧

小奧:在put的時(shí)候,首先對key做hash運(yùn)算康辑,計(jì)算出該key所在的index摄欲。

小奧:如果沒碰撞,直接放到數(shù)組中晾捏,如果碰撞了蒿涎,需要判斷目前數(shù)據(jù)結(jié)構(gòu)是鏈表還是紅黑樹哀托,根據(jù)不同的情況來進(jìn)行插入惦辛。

小奧:假設(shè)key是相同的,則替換到原來的值仓手。最后判斷哈希表是否滿了(當(dāng)前哈希表大小*負(fù)載因子)胖齐,如果滿了,則擴(kuò)容

小奧:在get的時(shí)候嗽冒,還是對key做hash運(yùn)算呀伙,計(jì)算出該key所在的index,然后判斷是否有hash沖突

小奧:假設(shè)沒有沖突直接返回添坊,假設(shè)有沖突則判斷當(dāng)前數(shù)據(jù)結(jié)構(gòu)是鏈表還是紅黑樹剿另,分別從不同的數(shù)據(jù)結(jié)構(gòu)中取出。

image.png

小茵:那在HashMap中是怎么判斷一個(gè)元素是否相同的呢?

小奧:首先會(huì)比較hash值雨女,隨后會(huì)用==運(yùn)算符和equals()來判斷該元素是否相同谚攒。

小奧:說白了就是:如果只有hash值相同,那說明該元素哈希沖突了氛堕,如果hash值和equals() || == 都相同馏臭,那說明該元素是同一個(gè)。

image.png

小茵:你說HashMap的數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表/紅黑樹讼稚,那什么情況拿下才會(huì)用到紅黑樹呢括儒?

小奧:當(dāng)數(shù)組的大小大于64且鏈表的大小大于8的時(shí)候才會(huì)將鏈表改為紅黑樹,當(dāng)紅黑樹大小為6時(shí)锐想,會(huì)退化為鏈表帮寻。

小奧:這里轉(zhuǎn)紅黑樹退化為鏈表的操作主要出于查詢和插入時(shí)對性能的考量。

小奧:鏈表查詢時(shí)間復(fù)雜度O(N)赠摇,插入時(shí)間復(fù)雜度O(1)规婆,紅黑樹查詢和插入時(shí)間復(fù)雜度O(logN)

image.png

小茵:你在日常開始中LinkedHashMap用的多嗎?

小奧:其實(shí)在日常開發(fā)中LinkedHashMap用得不多蝉稳。

小奧:在前面也提到了抒蚜,LinkedHashMap底層結(jié)構(gòu)是數(shù)組+鏈表+雙向鏈表,實(shí)際上它繼承了HashMap耘戚,在HashMap的基礎(chǔ)上維護(hù)了一個(gè)雙向鏈表嗡髓。

小奧:有了這個(gè)雙向鏈表,我們的插入可以是有序的收津,這里的有序不是指大小有序饿这,而是插入有序。

小奧:LinkedHashMap在遍歷的時(shí)候?qū)嶋H用的是雙向鏈表來遍歷的撞秋,所以LinkedHashMap的大小不會(huì)影響到遍歷的性能

小茵:那TreeMap呢长捧?

小奧:TreeMap在現(xiàn)實(shí)開發(fā)中用得也不多,TreeMap的底層數(shù)據(jù)結(jié)構(gòu)是紅黑樹

小奧:TreeMap的key不能為null(如果為null吻贿,那還怎么排序呢)串结,TreeMap有序是通過Comparator來進(jìn)行比較的,如果comparator為null舅列,那么就使用自然順序

image.png

小茵:再來講講線程安全的Map吧肌割?HashMap是線程安全的嗎?

小奧:HashMap不是線程安全的帐要,在多線程環(huán)境下把敞,HashMap有可能會(huì)有數(shù)據(jù)丟失和獲取不了最新數(shù)據(jù)的問題,比如說:線程Aput進(jìn)去了榨惠,線程Bget不出來奋早。

小奧:我們想要線程安全盛霎,可以使用ConcurrentHashMap

小奧:ConcurrentHashMap是線程安全的Map實(shí)現(xiàn)類,它在juc包下的耽装。

小奧:線程安全的Map實(shí)現(xiàn)類除了ConcurrentHashMap還有一個(gè)叫做Hashtable摩渺。

小奧:當(dāng)然了,也可以使用Collections來包裝出一個(gè)線程安全的Map剂邮。

小奧:但無論是Hashtable還是Collections包裝出來的都比較低效(因?yàn)槭侵苯釉谕鈱犹譻ynchronize)摇幻,所以我們一般有線程安全問題考量的,都使用ConcurrentHashMap

image.png

小奧:ConcurrentHashMap的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表/紅黑樹挥萌,它能支持高并發(fā)的訪問和更新绰姻,是線程安全的。

小奧:ConcurrentHashMap通過在部分加鎖和利用CAS算法來實(shí)現(xiàn)同步引瀑,在get的時(shí)候沒有加鎖狂芋,Node都用了volatile給修飾。

小奧:在擴(kuò)容時(shí)憨栽,會(huì)給每個(gè)線程分配對應(yīng)的區(qū)間帜矾,并且為了防止putVal導(dǎo)致數(shù)據(jù)不一致,會(huì)給線程的所負(fù)責(zé)的區(qū)間加鎖

小茵:你能給我講講JDK 7 和JDK8中HashMap和ConcurrentHashMap的區(qū)別嗎屑柔?

小奧:不能屡萤,我不會(huì)

小奧:我在學(xué)習(xí)的時(shí)候也看過JDK7的HashMap和ConcurrentHashMap,其實(shí)還是有很多不一樣的地方

候選者 比如JDK 7 的HashMap在擴(kuò)容時(shí)是頭插法掸宛,在JDK8就變成了尾插法死陆,在JDK7 的HashMap還沒有引入紅黑樹….

小奧:ConcurrentHashMap 在JDK7 還是使用分段鎖的方式來實(shí)現(xiàn),而JDK 8 就又不一樣了唧瘾。但JDK 7細(xì)節(jié)我大多數(shù)都忘了棕硫。

小奧:我就沒用過JDK 7的API岸裙,我想著現(xiàn)在最低應(yīng)該也是用JDK8了吧枉层?所以我就沒去仔細(xì)看了贰拿。

小茵:嗯...

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市求豫,隨后出現(xiàn)的幾起案子塌衰,更是在濱河造成了極大的恐慌,老刑警劉巖注祖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猾蒂,死亡現(xiàn)場離奇詭異,居然都是意外死亡是晨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門舔箭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罩缴,“玉大人蚊逢,你說我怎么就攤上這事◇镎拢” “怎么了烙荷?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長檬寂。 經(jīng)常有香客問我终抽,道長,這世上最難降的妖魔是什么桶至? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任昼伴,我火速辦了婚禮,結(jié)果婚禮上镣屹,老公的妹妹穿的比我還像新娘圃郊。我一直安慰自己,他們只是感情好女蜈,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布持舆。 她就那樣靜靜地躺著,像睡著了一般伪窖。 火紅的嫁衣襯著肌膚如雪逸寓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天覆山,我揣著相機(jī)與錄音席覆,去河邊找鬼。 笑死汹买,一個(gè)胖子當(dāng)著我的面吹牛佩伤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晦毙,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼生巡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了见妒?” 一聲冷哼從身側(cè)響起孤荣,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎须揣,沒想到半個(gè)月后盐股,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耻卡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年疯汁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卵酪。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幌蚊,死狀恐怖谤碳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情溢豆,我是刑警寧澤蜒简,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站漩仙,受9級特大地震影響搓茬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜队他,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一卷仑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漱挎,春花似錦系枪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膊夹,卻和暖如春衬浑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背放刨。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工工秩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人进统。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓助币,卻偏偏與公主長得像,于是被迫代替她去往敵國和親螟碎。 傳聞我的和親對象是個(gè)殘疾皇子眉菱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容