求求大廠給個Offer:Map面試題

前言

文本已收錄至個人GitHubhttps://github.com/ZhongFuCheng3y/3y,有300多篇原創文章,最近在連載面試系列!java

我,三歪,最近開始寫面試系列。我給這個面試系列取了一個名字,叫作《求求大廠給個Offergit

因此這篇文章叫作《求求大廠給個Offer:Map面試題github

接下來就開始吧。面試

面試現場

三歪:「我叫三歪,這幾年寫了300+原創技術文章,近1000頁的原創電子書和多個知識點的思惟導圖。個人願景是:只要關注我並三連的同窗均可以拿到大廠offer。個人....」算法

三歪:「Map在Java裏邊是一個接口,常見的實現類有HashMap、LinkedHashMap、TreeMap和ConcurrentHashMap」後端

三歪:「首先要明確的是:在Java裏邊,哈希表的結構是數組+鏈表的方式。HashMap底層數據機構是數組+鏈表/紅黑樹、LinkedHashMap底層數據結構是數組+鏈表+雙向鏈表、TreeMap底層數據結構是紅黑樹,而ConcurrentHashMap底層數據結構也是數組+鏈表/紅黑樹數組

面試官:「咱們先以HashMap開始吧,你能講講當你new一個HashMap的時候,會發生什麼嗎?」安全

三歪:「HashMap有幾個構造方法,但最主要的就是指定初始值大小和負載因子的大小。若是咱們不指定,默認HashMap的大小爲16,負載因子的大小爲0.75微信

三歪:「HashMap的大小隻能是2次冪的,假設你傳一個10進去,實際上最終HashMap的大小是16,你傳一個7進去,HashMap最終的大小是8,具體的實如今tableSizeFor能夠看到。咱們把元素放進HashMap的時候,須要算出這個元素所在的位置(hash)。在HashMap裏用的是位運算來代替取模,可以更加高效地算出該元素所在的位置。爲何HashMap的大小隻能是2次冪,由於只有大小爲2次冪時,才能合理用位運算替代取模。」數據結構

三歪:「而負載因子的大小決定着哈希表的擴容哈希衝突。好比如今我默認的HashMap大小爲16,負載因子爲0.75,這意味着數組最多隻能放12個元素,一旦超過12個元素,則哈希表須要擴容。怎麼算出是12呢?很簡單,就是16*0.75。每次put元素進去的時候,都會檢查HashMap的大小有沒有超過這個閾值,若是有,則須要擴容。」

三歪:「鑑於上面的說法(HashMap的大小隻能是2次冪),因此擴容的時候時候默認是擴原來的2倍」

三歪:「顯然擴容這個操做確定是耗時的,那我能不能把負載因子調高一點,好比我要調至爲1,那個人HashMap就等到16個元素的時候才擴容呢。顯然是能夠的,可是不推薦。負載因子調高了,這意味着哈希衝突的機率會增高,哈希衝突機率增高,一樣會耗時(由於查找的速度變慢了)」

三歪:「實現就在hash方法上,能夠發現的是,它是先算出正常的哈希值,而後與高16位作異或運算,產生最終的哈希值。這樣作的好處能夠增長了隨機性,減小了碰撞衝突的可能性。」

三歪:」在put的時候,首先對key作hash運算,計算出該key所在的index。若是沒碰撞,直接放到數組中,若是碰撞了,須要判斷目前數據結構是鏈表仍是紅黑樹,根據不一樣的狀況來進行插入。假設key是相同的,則替換到原來的值。最後判斷哈希表是否滿了(當前哈希表大小*負載因子),若是滿了,則擴容「

三歪:」在get的時候,仍是對key作hash運算,計算出該key所在的index,而後判斷是否有hash衝突,假設沒有直接返回,假設有則判斷當前數據結構是鏈表仍是紅黑樹,分別從不一樣的數據結構中取出。「

三歪:」首先會比較hash值,隨後會用==運算符和equals()來判斷該元素是否相同。說白了就是:若是隻有hash值相同,那說明該元素哈希衝突了,若是hash值和equals() || == 都相同,那說明該元素是同一個。「

三歪:」當數組的大小大於64且鏈表的大小大於8的時候纔會將鏈表改成紅黑樹,當紅黑樹大小爲6時,會退化爲鏈表。這裏轉紅黑樹退化爲鏈表的操做主要出於查詢和插入時對性能的考量。鏈表查詢時間複雜度O(N),插入時間複雜度O(1),紅黑樹查詢和插入時間複雜度O(logN)「

三歪:「其實在平常開發中LinkedHashMap用得很少。在前面也提到了,LinkedHashMap底層結構是數組+鏈表+雙向鏈表」,實際上它繼承了HashMap,在HashMap的基礎上維護了一個雙向鏈表。有了這個雙向鏈表,咱們的插入能夠是「有序」的,這裏的有序不是指大小有序,而是插入有序

三歪:「LinkedHashMap在遍歷的時候實際用的是雙向鏈表來遍歷的,因此LinkedHashMap的大小不會影響到遍歷的性能」

三歪:「TreeMap在現實開發中用得也很少,TreeMap的底層數據結構是紅黑樹,TreeMap的key不能爲null(若是爲null,那還怎麼排序呢),TreeMap有序是經過Comparator來進行比較的,若是comparator爲null,那麼就使用天然順序

三歪:「HashMap不是線程安全的,在多線程環境下,HashMap有可能會有數據丟失和獲取不了最新數據的問題,好比說:線程Aput進去了,線程Bget不出來。咱們想要線程安全,可使用ConcurrentHashMap」

三歪:「ConcurrentHashMap是線程安全的Map實現類,它在juc包下的。線程安全的Map實現類除了ConcurrentHashMap還有一個叫作Hashtable。固然了,也可使用Collections來包裝出一個線程安全的Map。但不管是Hashtable仍是Collections包裝出來的都比較低效(由於是直接在外層套synchronize),因此咱們通常有線程安全問題考量的,都使用ConcurrentHashMap」

三歪:「ConcurrentHashMap的底層數據結構是數組+鏈表/紅黑樹,它能支持高併發的訪問和更新,是線程安全的。ConcurrentHashMap經過在部分加鎖利用CAS算法來實現同步,在get的時候沒有加鎖,Node都用了volatile給修飾。在擴容時,會給每一個線程分配對應的區間,而且爲了防止putVal致使數據不一致,會給線程的所負責的區間加鎖」

三歪:「不能,我不會」

三歪:「我在學習的時候也看過JDK7的HashMap和ConcurrentHashMap,其實仍是有不少不同的地方,好比JDK 7 的HashMap在擴容時是頭插法,在JDK8就變成了尾插法,在JDK7 的HashMap尚未引入紅黑樹....ConcurrentHashMap 在JDK7 仍是使用分段鎖的方式來實現,而JDK 8 就又不同了。但JDK 7細節我大多數都忘了。」

三歪:「我就沒用過JDK 7的API,我想着如今最低應該也是用JDK8了吧?因此我就沒去仔細看了。要不我給你講講多線程相關的知識唄?

三歪:「哦」

題外話

針對此次的面試可能你想了解更多Map的細節,好比說Map基礎知識/HashMap/LinkedHashMap/TreeMap/ConcurrentHashMap的源碼,能夠在微信搜「Java3y」回覆「Map」便可獲取我以前寫的原創文章。

涵蓋Java後端全部知識點的開源項目,已有10K+ star!內含1000+頁原創電子書!!!

PDF文檔的內容均爲手打,有任何的不懂均可以直接來問我

相關文章
相關標籤/搜索