Java開發面試官終結者!HashMap高頻面試題總結,必拿下!
- HashMap 的工作原理
- HashMap 和 HashTable 的差別有什麼?
- HashMap 和 HashSet 的差別有什麼?
- 當兩個對象的 HashCode 相同會發生什麼?
- 如果兩個鍵的 HashCode 相同,你如何擷取值對象?
- 多線程情況下,調整 HashMap 的大小會有什麼問題?
- JDK8中的HashMap有哪些改動?
- JDK8中為什麼要使用紅黑樹?
- HashMap擴容機制是怎麼樣的,JDK7 與JDK8有什麼不同嗎?
- 為什麼重寫對象的Equals方法時,要重寫HashCode方法,跟HashMap有關系嗎?為什麼?
- 在使用HashMap的過程中我們應該注意些什麼問題?
- HashMap和Hashtable的差別

HashMap 的工作原理
首先 HashMap 是基于 hashing 的原理,我們知道 HashMap 有兩個常用的方法
put()
、
get()
,将鍵值對傳遞給
put()
方法時,它調用鍵對象的
hashCode()
方法來計算
hashcode
,然後找到 bucket 位置來儲存值對象。當擷取對象時,通過鍵對象的
equals()
方法找到正确的鍵值對,然後傳回值對象。
一般情況下,肯定會問如果不同的鍵對象的
hashcode
值相等會出現什麼樣的情況。他們肯定是存儲在同一個位置的連結清單中的,使用鍵對象的
equals()
方法找到鍵值對。
在 Java1.8 以後,HashMap 在數組、連結清單的基礎上又增加了紅黑樹的資料結構。在一個數組位置的連結清單長度大于 8 時資料結構轉換為紅黑樹的結構。
HashMap 和 HashTable 的差別有什麼?
HashMap 和 HashTable 都實作了 Map 接口,主要的差別集中線上程、同步、速度方面的差别。
-
HashMap 是非同步的,并且 HashMap 可以存儲鍵值為 null 的對象,允許最多隻有一個鍵可以為 null
對象、允許可以有多個值為 null。
- HashMap 是線程不安全的,HashTable 是線程安全的。
- 因為 HashTable 是多線程的,是以在單線程的情況下,HashMap 的速度要快一些。
- HashMap 不能保證存儲順序是不變的。
HashMap 和 HashSet 的差別有什麼?
- 兩者實作的接口不一樣,HashMap 實作的是 Map 接口、HashSet 則實作的是 Set 接口。
- HashMap 存儲的是鍵值對、HashSet 存儲的是對象。
- HashSet 的速度比 HashMap 的要慢一些。
- 計算 hashcode 值的方式不一樣,HashMap 使用鍵對象來計算、HashSet 使用它本身的對象元素來計算。
當兩個對象的 HashCode 相同會發生什麼?
因為hashcode相同,是以它們的bucket位置相同,‘碰撞’會發生。因為HashMap使用連結清單存儲對象,這個Entry(包含有鍵值對的Map.Entry對象)會存儲在連結清單中。
如果兩個鍵的 HashCode 相同,你如何擷取值對象?
當我們調用get()方法,HashMap會使用鍵對象的hashcode找到bucket位置,然後擷取值對象。找到bucket位置之後,會調用keys.equals()方法去找到連結清單中正确的節點,最終找到要找的值對象。
多線程情況下,調整 HashMap 的大小會有什麼問題?
由于線程不安全的原因,在多線程條件下調整 HashMap 的大小時會存在多個 HashMap 對象的競争關系,不知道要給哪一個調整大小。如此一來,多線程情況調整 HashMap 的大小就會陷入死循環的情況,在 Java1.5 以後就增加了 ConcurrentHashMap 的對象解決多線程等問題。
JDK8中的HashMap有哪些改動?
- JDK7中的底層實作是數組+連結清單,JDK8中使用的是數組+連結清單+紅黑樹。
- JDK7中擴容時有可能出現死鎖,JDK8中通過算法優化不會出現死鎖了。
- JDK8中對算哈希值的雜湊演算法進行了簡化以提高運算效率
JDK8中為什麼要使用紅黑樹?
因為JDK7中是用數組+連結清單來作為底層的資料結構的,但是如果資料量較多,或者hash算法的散列性不夠,可能導緻連結清單上的資料太多,導緻連結清單過長,考慮一種極端情況:如果hash算法很差,所有的元素都在同一個連結清單上。那麼在查詢資料的時候的時間複雜度和連結清單查詢的時間複雜度差不多是一樣的,我們知道連結清單的一個優點是插入快,但是查詢慢,是以如果HashMap中出現了很長的連結清單結構會影響整個HashMap的查詢效率,我們使用HashMap時插入和查詢的效率是都要具備的,而紅黑樹的插入和查詢效率處于完全平衡二叉樹和連結清單之間,是以使用紅黑樹是比較合适的。
HashMap擴容機制是怎麼樣的,JDK7 與JDK8有什麼不同嗎?
首先,我們需要知道HashMap為什麼需要擴容,道理很簡單,HashMap底層是用數組+連結清單實作的,而數組是預先就已經配置設定好記憶體的,如果需要對數組進行擴容,需要重新開辟一個新的數組再将舊數組上的元素進行轉移,如果不進行擴容,那麼會導緻HashMap的連結清單過長,查詢效率降低,是以需要對數組進行擴容。
在JDK7中,HashMap擴容的條件是 (size >= threshold) && (null !=table[bucketIndex]) , size 為HashMap目前的容量, threshold 初始化值為12, table[bucketIndex] 代表所put進來的key所對應的數組上的元素,是以在JDK7中擴容條件是當當Put操操作傳入的 作傳入的Key值所對應的數組位置上不為空時并且目前容量大于等于了擴容的門檻值時才進行擴容 值所對應的數組位置上不為空時并且目前容量大于等于了擴容的門檻值時才進行擴容,JDK7中的擴容思路是:開辟一個新的數組,數組大小為原數組的兩倍,然後再将數組上的連結清單與元素轉移到新數組上,此過程可能會出現死鎖。
JDK8中的擴容條件比JDK7中要少,隻有目前容量大于等于了擴容的門檻值時才進行擴容 目前容量大于等于了擴容的門檻值時才進行擴容,并且擴容的思路也發生了變化,思路比較複雜。
為什麼重寫對象的Equals方法時,要重寫HashCode方法,跟HashMap有關系嗎?為什麼?
在使用HashMap的過程中我們應該注意些什麼問題?
- HashMap的擴容機制是很影響效率的,是以如果事先能确定有多少個元素需要存儲,那麼建議在初始化HashMap時對數組的容量也進行初始化,防止擴容。
- HashMap中使用了對象的hashcode方法,而且很關鍵,是以再重寫對象的equals時建議一定要重寫hashcode方法。
- 如果是用對象作為HashMap的key,那麼請将對象設定為final,以防止對象被重新指派,因為一旦重新指派其實就代表了一個新對象作為了key,因為兩個對象的hashcode可能不同。
HashMap和Hashtable的差別
- HashMap 是非線程安全的,HashTable 是線程安全的;HashTable 内部的方法基本都經過 synchronized修飾
- 因為線程安全的問題,HashMap 要比 HashTable 效率高一點。另外,HashTable 基本被淘汰,不要在代碼中使用它
- HashMap 中,null 可以作為鍵,這樣的鍵隻有一個,可以有一個或多個鍵所對應的值為 null。但是在HashTable 中put 進的鍵值隻要有一個 null,直接抛出 NullPointerException。
- JDK1.8 以後的 HashMap在解決哈希沖突時有了較大的變化,當連結清單長度大于門檻值(預設為8)時,将連結清單轉化為紅黑樹,以減少搜尋時間。Hashtable沒有這樣的機制。 最後:祝大家都能在秋招中找到自己合适的工作,飛黃騰達!!
Java開發面試官終結者!HashMap高頻面試題總結,必拿下! Java開發面試官終結者!HashMap高頻面試題總結,必拿下!