HashMap面試筆記整理

2021-10-24 04:53:48 字數 3444 閱讀 3243

1.hashmap在jdk1.8以前和以後的區別

​ 1):jdk1.8以前,hashmap的底層實現是陣列+鍊錶,它的缺點就是即使雜湊函式用的再好,也很難達到百分百均勻分布,而且當很多元素放在乙個桶中時,鍊錶會變得很長,此時遍歷的時間複雜度為o(n),jdk1.8以前,hashmap採用的是頭插法,如果插入時,陣列位置中已經有了元素,jdk1.8以前會插入到陣列中,原始節點變成新節點的後續節點,插入時,先判斷是否需要擴容,然後再插入。擴容時,1.8以前需要對原陣列中的元素進行重新的hash定位帶行的陣列位置

​ 2):jdk1.8以後,hashmap的底層實現是陣列+鍊錶+紅黑樹,若桶中的鍊錶個數超過8個,就會變成紅黑樹,遍歷查詢,由於使用紅黑樹,此時時間複雜度為o(logn),使用效能上得到了提公升。jdk1.8以後,hashmap採用的是尾插法,如果插入時,陣列位置中已經有了元素,jdk1.8以後會遍歷陣列,將元素放在最後,插入時,先插入再擴容。擴容時,1.8以後採用的是簡單的判斷邏輯,位置不變或索引+舊容量大小

2.hashmap資料插入原理

​ 1):判斷陣列是否為空,為空就初始化

​ 2):不為空就計算出k的hash值,通過(n-1)& hash計算出應當存放在陣列的下標 index

​ 3):檢視table[index]是否存在資料,沒有資料就構建乙個node節點存放在table[index]中

​ 4):如果有資料,說明出現了hash衝突,然後判斷key值是否相等,如果相等就替換原來的value值

​ 5):如果不相等,判斷當前節點是不是樹型節點,如果是樹型節點,就建立樹型節點插入到紅黑樹中

​ 6):如果不是樹型節點,就建立普通的node節點加入鍊錶中,判斷鍊錶長度是否大於8,並且陣列長度大於64,大於的話鍊錶變為紅黑樹

​ 7):插入完成後要判斷節點數是否大於閾值,如果大於,則擴容陣列為原來的2倍

3.hashmap怎麼設定初始容量大小的

​ 一般new hashmap() 不傳值得話,預設容量為16負載因子為0.75,如果自己傳乙個值k,那麼初始值為大於k的2的整數次方,例如:k=7,那麼預設容量就是8(2的3次方),如果k=10,那麼預設容量就是16(2的4次方).

4.hashmap擴容機制

1):什麼時候擴容

​ 當容器新增元素時,會判斷元素的個數,如果大小等於閾值(閾值=陣列長度*負載因子 例如長度16的陣列的閾值為:16x0.75=12,即元素個數大於等於12時就會自動擴容)時就會自動擴容

2):jdk1.7和jdk1.8擴容的區別

​ jdk1.7):如果原有table長度已經達到了上限,就不再擴容了。

​ 如果還未達到上限,則建立乙個新的table,並呼叫transfer方法,

​ transfer方法的作用是把原table的node放到新的table中,使用的是頭插法,也就是說,新table中煉表的順序和舊列表中是相反 的(例如:原陣列是3–>5–>7,新陣列就是7–>5–>3),在hashmap執行緒不安全的情況下,這種頭插法可能會導致環狀節點(例如:a執行緒在插入節點b,b執行緒也在插入,遇到容量不夠開始擴容,重新hash,放置元素,採用頭插法,後遍歷到的b節點放入了頭部,這樣形成了環)。

​ jdk1.8):新陣列長度是原陣列長度的兩倍(如果原陣列是2的n次倍,新陣列就是2的n+1次倍);

​ 新陣列和原陣列鍊錶順序一致(原陣列是3–>5–>7,新陣列還是3–>5–>7);

​ 正常情況下,計算節點在table中的下標的方法是:hash&(oldtable.length-1),擴容之後,table長度翻倍,計算table下標的方法是hash&(newtable.length-1),也就是hash&(oldtable.length*2-1),於是我們有了這樣的結論:這新舊兩次計算下標的結果,要不然就相同,要不然就是新下標等於舊下標加上舊陣列的長度

5.hashmap執行緒安全問題

​ 1):hashmap是執行緒不安全的,多執行緒情況下:jdk1.7會出現死迴圈,資料丟失,資料覆蓋的問題jdk1.8會出現資料覆蓋的問題(以1.8為例,當a執行緒判斷index位置為空後正好掛起,b執行緒開始往index位置的寫入節點資料,這時a執行緒恢復現場,執行賦值操作,就把a執行緒的資料給覆蓋了;還有++size這個地方也會造成多執行緒同時擴容等問題。)

​ 2):執行緒安全的map集合有:hashtable,collections.synchronizedmap,concurrenthashmap(使用最廣泛)

hashtable:hashtable是直接在操作方法上加synchronized關鍵字,鎖住整個陣列,粒度比較大。

collections.synchronizedmap:是使用collections集合工具的內部類,通過傳入map封裝出乙個synchronizedmap物件,內部定義了乙個物件鎖,方法內通過物件鎖實現;

concurrenthashmap:使用分段鎖,降低了鎖粒度,讓併發度大大提高。

6.concurrenthashmap的分段鎖的實現原理

​ concurrenthashmap成員變數使用volatile 修飾,免除了指令重排序,同時保證記憶體可見性,另外使用cas操作和synchronized結合實現賦值操作,多執行緒操作只會鎖住當前操作索引的節點。

7.鍊錶轉紅黑樹的閾值是多少,紅黑樹轉鍊錶的閾值是多少

​ 鍊錶轉紅黑樹的閾值是8,紅黑樹轉鍊錶的閾值是6

8.hashmap內部節點是否有序

​ 1):無序,根據hash值隨機插入

​ 2):有序的map集合有哪些:linkedhashmap 和 treemap

linkedhashmap如何實現有序:

​ linkedhashmap內部維護了乙個單鏈表,有頭尾節點,同時linkedhashmap節點entry內部除了繼承hashmap的node屬性,還有before 和 after用於標識前置節點和後置節點。可以實現按插入的順序或訪問順序排序。

treemap如何實現有序:

​ treemap是按照key的自然順序或者comprator的順序進行排序,內部是通過紅黑樹來實現。所以要麼key所屬的類實現comparable介面,或者自定義乙個實現了comparator介面的比較器,傳給treemap用於key的比較。

面試筆記 HashMap擴容機制

擴容必須滿足兩個條件 1 存放新值的時候當前已有元素的個數必須大於等於閾值 2 存放新值的時候當前存放資料發生hash碰撞 當前key計算的hash值換算出來的陣列下標位置已經存在值 如果需要擴容,呼叫擴容的方法resize void resize int newcapacity entry new...

面試筆記1

今天開始準備找實習了,開始準備實習的東西了。接下來開始寫今天看到的小知識。1.宣告,定義,初始化 宣告是指extern int i 定義是指int i 初始化int i 0 區別在於,宣告不分配儲存空間,在這裡編譯的時候是不管的,只有當需要用到i的時候才去檢查。定義的話,就分配一塊空間給它。初始化的...

面試筆記3

有關函式宣告的空間問題 乙個函式在宣告後如果不寫函式體是不會分配空間的,所以實現這個函式也就相當於初始化函式這個變數,同時也就引申出了變數如果只宣告的話是不會分配的空間的?可以這麼認為,因為靜態變數跟全域性變數都是自動初始化為0的。另外在c語言中 void fun 等同於 void fun 在c 語...