HashMap面試專題,不看後悔

之前也零零碎碎髮過一些HashMap的文章,此次簡短總結一下有關HashMap的重要考點,這也是求職面試的常常問的,看完記得點個贊和在看哦~
java

一、Hash的概念 

任意長度的輸入經過散列算法以後映射成固定長度的輸出面試

二、Hash衝突 

當關鍵字集合很大時(key的數量不少的時候),關鍵字值不一樣的元素可能會映像到哈希表的同一地址上,即K1!=K2,但f(K1)=f(K2),這種現象稱爲hash衝突,實際中衝突是不可避免的,只能經過改進哈希函數的性能來減小衝突算法

三、你認爲好的Hash算法的點應該有哪些? 

(1)效率得高,作到長文本也能高效計算出Hash值

(2)根據Hash值不能逆推出原文

(3)兩次輸入,若是有一點不一樣也得保證Hash值是不一樣的

(4)儘量要分散,由於在table中slot大部分都處於空閒狀態時要儘量下降Hash衝突數組

四、HashMap的存儲結構長啥樣?

JDK1.8:安全

(1)數組+鏈表+紅黑樹構成,每一個數據單元爲一個Node結構,Node結構中有key字段、value字段、next字段、hash字段
(2)next字段就是發生Hash衝突的時候,當前桶位中的Node與衝突Node鏈接成一個鏈表所須要的字段

JDK1.7:微信

數組+鏈表多線程

4五、若是建立HashMap的時候沒有指定HashMap散列表的長度,初始長度爲多少?

在JDK 8中,關於默認容量的定義爲:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16 ,其故意把16寫成1<<4,就是提醒開發者,這個地方要是2的冪app

(1)爲啥用位運算呢?直接寫16很差麼?
這樣是爲了位運算的方便,位與運算比算數計算的效率高了不少,之因此選擇16,是爲了服務將Key映射到index的算法。編輯器

(2)那爲啥用16不用別的呢?
由於在使用不是2的冪的數字的時候,Length-1的值是全部二進制位全爲1,這種狀況下,index的結果等同於HashCode後幾位的值。這個值既不能過小,也不能太大
過小了就有可能頻繁發生擴容,影響效率,太大了又浪費空間,不划算。函數

只要輸入的HashCode自己分佈均勻,Hash算法的結果就是均勻的。

這是爲了實現均勻分佈

六、散列表是New HashMap()的時候建立的,仍是何時建立的?

散列表是懶加載機制,只有在第一次put數據的時候才建立(JDK1.8,JDK1.7是直接加載散列表

七、負載因子默認是多少,有啥做用?爲何負載因子爲0.75?何時進行擴容(resize)?

(1)默認爲0.75,用於計算擴容閾值
(2)loadFactor是負載因子,表示HashMap滿的程度,默認值爲0.75f,設置成0.75有一個好處,那就是0.75正好是3/4而capacity又是2的冪。因此,兩個數的乘積都是整數
(3)影響擴容主要有兩個因素:
  Capacity:HashMap當前長度。
  LoadFactor:負載因子,默認值0.75f。
  怎麼理解呢,就好比當前的容量大小爲100,當你存進第76個的時候,判斷髮現大於擴容閾值100*0.75=75須要進行resize了,那就進行擴容,可是HashMap的擴容也不是簡單的擴大點容量這麼簡單的。

八、擴容?它是怎麼擴容的呢?

分爲兩步

(1)擴容:建立一個新的Entry空數組,長度是原數組的2倍

(2)ReHash:遍歷原Entry數組,把全部的Entry從新Hash到新數組

爲何要從新Hash呢,直接複製過去不香麼?

是由於長度擴大之後,Hash的規則也隨之改變。

好比原來長度(Length)是8你位運算出來的值是2 ,新的長度是16你位運算出來的值明顯不同了。

九、鏈表轉化爲紅黑樹的條件

(1)鏈表長度達到8
(2)當前散列表長度達到64
以上兩個條件同時知足鏈表纔會轉化爲紅黑樹,若是僅僅鏈表長度達到8,它不會發生鏈表轉紅黑樹,只會發生一次散列表擴容(resize)

十、Node對象裏面的hash字段的值是key對象的hashcode的返回值嗎?

不是的,經過key的hashcode的高16位異或低16位獲得的新值,這樣即便數組table的length比較小的時候,也能保證高低bit都參與到Hash的計算中,避免高16位浪費沒起到做用,儘量的獲得一個均勻分佈的hash

十一、爲啥咱們重寫equals方法的時候須要重寫hashCode方法呢?你能用HashMap給我舉個例子麼?

由於在java中,全部的對象都是繼承於Object類。Ojbect類中有兩個方法equalshashCode,這兩個方法都是用來比較兩個對象是否相等的。

在未重寫equals方法咱們是繼承了object的equals方法,那裏的 equals是比較兩個對象的內存地址,顯然咱們new了2個對象內存地址確定不同

好比發生Hash衝突的時候,咱們去get,他就是根據key去hash而後計算出index,找到了2,那我怎麼找到具體的」電腦「仍是」腦電「呢?

equals!是的,因此若是咱們對equals方法進行了重寫,建議必定要對hashCode方法重寫,以保證相同的對象返回相同的hash值,不一樣的對象返回不一樣的hash值

十二、HashMap的put數據的流程

1三、爲何java8之後鏈表數據超過8之後,就改爲紅黑樹存儲?

這就涉及到拒接服務攻擊了,好比某些人經過找到你的hash碰撞值,來讓你的HashMap不斷地產生碰撞,那麼相同key位置的鏈表就會不斷增加,當你須要對這個HashMap的相應位置進行查詢的時候,就會去循環遍歷這個超級大的鏈表,性能及其地下。java8使用紅黑樹來替代超過8個節點數的鏈表後,查詢方式性能獲得了很好的提高,從原來的是O(n)到O(logn),容器中節點分佈在hash桶中的頻率遵循泊松分佈,桶的長度超過8的機率很是很是小(約爲10萬分之一),因此做者應該是根據機率統計而選擇了8做爲閥值。

1四、Hashmap的結構,1.7和1.8有哪些區別?

(1)JDK1.7用的是頭插法,而JDK1.8及以後使用的都是尾插法,那麼他們爲何要這樣作呢?
由於JDK1.7是用單鏈表進行的縱向延伸,當採用頭插法時會容易出現逆序且環形鏈表死循環問題。可是在JDK1.8以後是由於加入了紅黑樹使用尾插法,可以避免出現逆序且鏈表死循環的問題。

(2)擴容後數據存儲位置的計算方式也不同:1. 在JDK1.7的時候是直接用hash值和須要擴容的二進制數進行&(這裏就是爲何擴容的時候爲啥必定必須是2的多少次冪的緣由所在,由於若是隻有2的n次冪的狀況時最後一位二進制數才必定是1,這樣能最大程度減小hash碰撞)(hash值 & length-1)

1五、首先HashMap是線程不安全的,其主要體如今哪裏?

(1)在jdk1.7中,在多線程環境下,擴容時會形成環形鏈或數據丟失

(2)在jdk1.8中,在多線程環境下,會發生數據覆蓋的狀況。


若是你喜歡本文,
請長按二維碼,關注 Java技術大聯盟.
轉發至 朋友圈 ,是對我最大的支持。

點個 在看 
喜歡是一種感受
在看是一種支持

本文分享自微信公衆號 - Java技術大聯盟(jingdakunye520)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索