您好,登錄后才能下訂單哦!
這篇文章主要介紹“有哪些HashMap面試專題”,在日常操作中,相信很多人在有哪些HashMap面試專題問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”有哪些HashMap面試專題”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
將任意長度的輸入通過散列算法之后映射成固定長度的輸出。
當關鍵字集合很大時(key的數量很多的時候),關鍵字值不同的元素可能會映像到哈希表的同一地址上,即K1!=K2,但f(K1)=f(K2),這種現象稱為hash沖突,實際中沖突是不可避免的,只能通過改進哈希函數的性能來減少沖突。
(4)盡可能要分散,因為在table中slot大部分都處于空閑狀態時要盡可能降低Hash沖突
JDK1.8:
(1)數組+鏈表+紅黑樹構成,每個數據單元為一個Node結構,Node結構中有key字段、value字段、next字段、hash字段
(2)next字段就是發生Hash沖突的時候,當前桶位中的Node與沖突Node連接成一個鏈表所需要的字段
JDK1.7:
數組+鏈表
在JDK 8中,關于默認容量的定義為:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16 ,其故意把16寫成1<<4,就是提醒開發者,這個地方要是2的冪。
(1)為啥用位運算呢?直接寫16不好么?
這樣是為了位運算的方便,位與運算比算數計算的效率高了很多,之所以選擇16,是為了服務將Key映射到index的算法。
(2)那為啥用16不用別的呢?
因為在使用不是2的冪的數字的時候,Length-1的值是所有二進制位全為1,這種情況下,index的結果等同于HashCode后幾位的值。這個值既不能太小,也不能太大。
太小了就有可能頻繁發生擴容,影響效率,太大了又浪費空間,不劃算。
只要輸入的HashCode本身分布均勻,Hash算法的結果就是均勻的。
這是為了實現均勻分布。
散列表是懶加載機制,只有在第一次put數據的時候才創建(JDK1.8,JDK1.7是直接加載散列表)
(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)
不是的,通過key的hashcode的高16位異或低16位得到的新值,這樣即使數組table的length比較小的時候,也能保證高低bit都參與到Hash的計算中,避免高16位浪費沒起到作用,盡可能的得到一個均勻分布的hash。
因為在java中,所有的對象都是繼承于Object類。Ojbect類中有兩個方法equals、hashCode,這兩個方法都是用來比較兩個對象是否相等的。
在未重寫equals方法我們是繼承了object的equals方法,那里的 equals是比較兩個對象的內存地址,顯然我們new了2個對象內存地址肯定不一樣
比如發生Hash沖突的時候,我們去get,他就是根據key去hash然后計算出index,找到了2,那我怎么找到具體的”電腦“還是”腦電“呢?
equals!是的,所以如果我們對equals方法進行了重寫,建議一定要對hashCode方法重寫,以保證相同的對象返回相同的hash值,不同的對象返回不同的hash值。
這就涉及到拒接服務攻擊了,比如某些人通過找到你的hash碰撞值,來讓你的HashMap不斷地產生碰撞,那么相同key位置的鏈表就會不斷增長,當你需要對這個HashMap的相應位置進行查詢的時候,就會去循環遍歷這個超級大的鏈表,性能及其地下。java8使用紅黑樹來替代超過8個節點數的鏈表后,查詢方式性能得到了很好的提升,從原來的是O(n)到O(logn),容器中節點分布在hash桶中的頻率遵循泊松分布,桶的長度超過8的概率非常非常小(約為10萬分之一),所以作者應該是根據概率統計而選擇了8作為閥值。
(1)JDK1.7用的是頭插法,而JDK1.8及之后使用的都是尾插法,那么他們為什么要這樣做呢?
因為JDK1.7是用單鏈表進行的縱向延伸,當采用頭插法時會容易出現逆序且環形鏈表死循環問題。但是在JDK1.8之后是因為加入了紅黑樹使用尾插法,能夠避免出現逆序且鏈表死循環的問題。
(2)擴容后數據存儲位置的計算方式也不一樣:1. 在JDK1.7的時候是直接用hash值和需要擴容的二進制數進行&(這里就是為什么擴容的時候為啥一定必須是2的多少次冪的原因所在,因為如果只有2的n次冪的情況時最后一位二進制數才一定是1,這樣能最大程度減少hash碰撞)(hash值 & length-1)
(1)在jdk1.7中,在多線程環境下,擴容時會造成環形鏈或數據丟失。
(2)在jdk1.8中,在多線程環境下,會發生數據覆蓋的情況。
到此,關于“有哪些HashMap面試專題”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。