別怕,遇到Map原始碼面試題這樣答就對了

2021-10-06 19:46:56 字數 4358 閱讀 1768

眾所周知,在面試大廠的過程中,hashmap原始碼是必問的,當然有些還會問treemap 和 linkedhashmap。如果事先沒有準備,當然會瞬間懵逼,因此在面試前建議大家抽出時間準備準備,很容易就能續寫自己的高光時刻。

在開始之前,強烈推薦閱讀這個多達兩萬字的hashmap原始碼分析文章,是的,你想要的,它都有!

強化基礎,hashmap原始碼全角度詳細解析

別的廢話就不多說了,下面開始見招拆招。

3、hashmap、treemap、linkedhashmap有什麼相同點和不同點?

4、hashmap原始碼裡面是怎麼解決hash衝突的?

5、那你說說hashmap是怎麼擴容的?

6、那為什麼鍊錶個數大於等於8時,鍊錶要轉化成紅黑樹了?為什麼不是6,不是10?

7、紅黑樹什麼時候轉變成鍊錶?

8、hashmap 在 put 時,如果陣列中已經有了這個 key,我不想把 value 覆蓋怎麼辦?取值時,如果得到的 value 是空時,想返回預設值怎麼辦?

9、刪除資料時可以使用foreach進行刪除嗎?

10、linkedhashmap中的lru是怎麼實現的?

11、為什麼使用treemap時推薦把key實現 comparable 介面?什麼情況下不需要去實現這個介面呢?

hashmap 底層是陣列 + 鍊錶 + 紅黑樹的資料結構,陣列的主要作用是方便快速查詢,時間複雜度是 o(1),預設大小是16,陣列的下標索引是通過 key 的 hashcode 計算出來的,陣列元素叫做 node,當多個 key 的 hashcode 一致,但 key 值不同時,單個 node 就會轉化成鍊錶,鍊錶的查詢複雜度是 o(n),當鍊表的長度大於等於 8 並且陣列的大小超過 64 時,鍊錶就會轉化成紅黑樹,紅黑樹的查詢複雜度是 o(log(n)),簡單來說,最壞的查詢次數相當於紅黑樹的最大深度。

static

final

inthash

(object key)

可以得出key在陣列中的位置公式:tab[

(n -1)

& hash]

如上**是 hashmap 的hash 演算法。

這其實可以理解為乙個數學問題,原始碼中就是通過以上**來計算 hash 的,首先計算出 key 的 hashcode,因為 key 是 object,所以會根據 key 的不同型別進行 hashcode 的計算,接著計算h ^ (h >>> 16) ,這麼做的好處是使大多數場景下,算出來的 hash 值比較分散。

一般來說,hash 值算出來之後,要計算當前 key 在陣列中的索引下標位置時,可以採用取模的方式,就是索引下標位置 = hash 值 % 陣列大小,這樣做的好處,就是可以保證計算出來的索引下標值可以均勻的分布在陣列的各個索引位置上,但取模操作對於處理器的計算是比較慢的,數學上有個公式,當 b 是 2 的冪次方時,a % b = a &(b-1),所以此處索引位置的計算公式我們可以更換為: (n-1) & hash。

當你順利的答出了上面這個問題後,可能面試官會接著追問如下四個問題,別怕,接著說就好了!

1. 為什麼不用 key對陣列大小取餘,而是需要用 key 的 hash 值對陣列大小取餘?

如果 key 是數字,直接用 key % 陣列大小是完全沒有問題的,但我們的 key 還有可能是字串,是複雜物件,這時候用 字串或複雜物件 % 陣列大小是不行的,所以需要先計算出 key 的 hash 值。

2. 計算 hash 值時,為什麼需要右移 16 位?

hash 演算法是 h ^ (h >>> 16),為了使計算出的 hash 值更分散,所以選擇先將 h 無符號右移 16 位,然後再於 h 異或時,就能達到 h 的高 16 位和低 16 位都能參與計算,減少了碰撞的可能性。

3. 為什麼把取模操作換成了 & 操作?

key.hashcode() 算出來的 hash 值還不是陣列的索引下標,為了隨機的計算出索引的下表位置,我們還會用 hash 值和陣列大小進行取模,這樣子計算出來的索引下標比較均勻分布。

取模操作處理器計算比較慢,處理器對 & 操作就比較擅長,換成了 & 操作,是有數學上證明的支撐,為了提高了處理器處理的速度。

4. 為什麼提倡陣列大小是 2 的冪次方?

因為只有大小是 2 的冪次方時,才能使 hash 值 % n(陣列大小) == (n-1) & hash 公式成立。

3.1、首先是相同點:

三者在特定的情況下,都會使用紅黑樹;

底層的 hash 演算法相同;

在迭代的過程中,如果 map 的資料結構被改動,都會報 concurrentmodificationexception 的錯誤。

3.2、其次是不同點:

hashmap資料結構以陣列為主,查詢非常快,treemap 資料結構以紅黑樹為主,利用了紅黑樹左小右大的特點,可以實現 key 的排序,linkedhashmap 在 hashmap 的基礎上增加了鍊錶的結構,實現了插入順序訪問和最少訪問刪除兩種策略;

由於三種 map 底層資料結構的差別,導致了三者的使用場景的不同:

hash 衝突指的是 key 值的 hashcode 計算相同,但 key 值不同的情況。

通過學習原始碼,可以知道如果桶中元素原本只有乙個或已經是鍊錶了,新增元素直接追加到鍊錶尾部;如果桶中元素已經是鍊錶,並且鍊錶個數大於等於 8 時,此時有兩種情況:

這裡不僅僅判斷鍊錶個數大於等於 8,還判斷了陣列大小,陣列容量小於 64 沒有立即轉化的原因,猜測主要是因為紅黑樹占用的空間比煉表大很多,轉化也比較耗時,所以陣列容量小的情況下衝突嚴重,可以先嘗試擴容,通過擴容看看能不能解決根本的問題。

擴容需要滿足一定的條件,主要是兩種:

put 時,發現陣列為空,進行初始化擴容,預設擴容大小為 16;

put 成功後,發現現有陣列大小大於擴容的門閥值時,進行擴容,擴容為老陣列大小的 2 倍。

其次擴容的門閥是 threshold,每次擴容時 threshold 都會被重新計算,門閥值等於陣列的大小 * 影響因子(0.75)。新陣列初始化之後,需要將老陣列的值拷貝到新陣列上,鍊錶和紅黑樹都有自己拷貝的方法。

hashmap在原始碼的類注釋裡對為什麼是8有過解釋,是這樣說的:通過泊松分布公式計算,正常情況下,鍊錶個數出現 8 的概念不到千萬分之一,所以說正常情況下,鍊錶都不會轉化成紅黑樹,這樣設計的目的,是為了防止非正常情況下,比如 hash 演算法出了問題時,導致鍊錶個數輕易大於等於 8 時,仍然能夠快速遍歷。

雖然成紅黑樹,可以使遍歷的時間複雜度降低,但是也要考慮到轉化成紅黑樹,有空間和轉化耗時的成本,因此不能輕易的轉化。

當節點的個數小於等於 6 時,紅黑樹會自動轉化成鍊錶,主要還是考慮紅黑樹的空間成本問題,當節點個數小於等於 6 時,遍歷鍊錶也很快,所以紅黑樹會重新變成鍊錶。

如果陣列有了 key,但不想覆蓋 value ,可以選擇putifabsent 方法,這個方法有個內建變數onlyifabsent,內建是 true ,就不會覆蓋,我們平時使用的 put 方法,內建 onlyifabsent 為 false,是允許覆蓋的。

取值時,如果為空,想返回預設值,可以使用getordefault 方法,方法第一引數為 key,第二個引數為你想返回的預設值,如 map.getordefault(「2」,「0」),當 map 中沒有 key 為 2 的值時,會預設返回 0,而不是空。

不可以,因為在for迴圈之前modcount會賦值給mc,但是迴圈之後,moucount發生了變化,但是mc卻是變化之前的值,這點和arraylist刪除資料類似,都是不可以用簡單的foreach刪除的。

首先lru的英文全稱是least recently used,翻譯過來就是最近最少訪問,在 linkedhashmap 中,也叫做最少訪問刪除策略,我們可以通過removeeldestentry 方法設定一定的策略,使最少被訪問的元素,在適當的時機被刪除,原理是在 put 方法執行的最後,linkedhashmap 會去檢查這種策略,如果滿足策略,就刪除頭節點。

保證頭節點就是最少訪問的元素的原理是:linkedhashmap 在 get 的時候,都會把當前訪問的節點,移動到鍊錶的尾部,慢慢的,就會使頭部的節點都是最少被訪問的元素了。

這是因為 treemap 的底層就是通過排序來比較兩個 key 的大小的,所以推薦 key 實現 comparable 介面,是為了往你希望的排序順序上發展。

如果我們的key的型別本身已經實現了 comparable 介面,比如string以及一些包裝型別如long、double、short 等等就不需要我們再去實現 comparable 介面了。

String原始碼分析,解讀面試題

1.string類不可被繼承 string類定義 public final class string2.string是不可變物件 string屬性 private final char value private int hash 3.下面的 建立了幾個string例項?string s new s...

面試中關於執行緒原始碼相關的面試題

關於執行緒方面的面試題,大部分都是概念題,我們需要大概的清楚這些概念,和面試官達成共識即可 1 建立子執行緒時,子執行緒是得不到父執行緒的 threadlocal,有什麼辦法可以解決這個問題?當父執行緒的 inheritablethreadlocals 的值不為空時 會把 inheritableth...

這樣出ORACLE的面試題

剛剛在翻閱 這裡暫且說翻閱,其實只是在電腦上看 oracle手冊的時候,突發奇想。如果oracle的面試題這樣出如何?用過oracle的dataguard嗎?被面試者一定會說,用過。好的,告訴相關的內容在oracle官方手冊的什麼書中介紹?知道oracle的expdp吧,能告訴我在oracle官方手...