這幾道Java集合框架面試題在面試中幾乎必問

2021-08-25 17:16:11 字數 3380 閱讀 1531

主要內容:

arraylist 與 linkedlist 異同

arraylist 與 vector 區別

hashmap的底層實現

hashmap 和 hashtable 的區別

hashmap 的長度為什麼是2的冪次方

hashset 和 hashmap 區別

concurrenthashmap 和 hashtable 的區別

concurrenthashmap執行緒安全的具體實現方式/底層具體實現

集合框架底層資料結構總結

雙向鍊錶也叫雙鏈表,是鍊錶的一種,它的每個資料結點中都有兩個指標,分別指向直接後繼和直接前驅。所以,從雙向鍊錶中的任意乙個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。一般我們都構造雙向迴圈鍊錶,如下圖所示,同時下圖也是linkedlist 底層使用的是雙向迴圈鍊錶資料結構。

vector類的所有方法都是同步的。可以由兩個執行緒安全地訪問乙個vector物件、但是乙個執行緒訪問vector的話**要在同步操作上耗費大量的時間。

arraylist不是同步的,所以在不需要保證執行緒安全時時建議使用arraylist。

jdk1.8 之前 hashmap 由陣列+鍊錶組成的(「鍊錶雜湊」即陣列和鍊錶的結合體),陣列是 hashmap 的主體,鍊錶則是主要為了解決雜湊衝突而存在的(hashmap 採用「拉鍊法也就是鏈位址法」解決衝突),如果定位到的陣列位置不含鍊錶(當前 entry 的 next 指向 null ),那麼對於查詢,新增等操作很快,僅需一次定址即可;如果定位到的陣列包含鍊錶,對於新增操作,其時間複雜度依然為 o(1),因為最新的 entry 會插入鍊錶頭部,即需要簡單改變引用鏈即可,而對於查詢操作來講,此時就需要遍歷鍊錶,然後通過 key 物件的 equals 方法逐一比對查詢.

所謂「拉鍊法」就是將鍊錶和陣列相結合。也就是說建立乙個鍊錶陣列,陣列中每一格就是乙個鍊錶。若遇到雜湊衝突,則將衝突的值加到鍊錶中即可。

相比於之前的版本, jdk1.8之後在解決雜湊衝突時有了較大的變化,當鍊表長度大於閾值(預設為8)時,將鍊錶轉化為紅黑樹,以減少搜尋時間。

treemap、treeset以及jdk1.8之後的hashmap底層都用到了紅黑樹。紅黑樹就是為了解決二叉查詢樹的缺陷,因為二叉查詢樹在某些情況下會退化成乙個線性結構。

推薦閱讀:執行緒是否安全:hashmap 是非執行緒安全的,hashtable 是執行緒安全的;hashtable 內部的方法基本都經過synchronized修飾。(如果你要保證執行緒安全的話就使用 concurrenthashmap 吧!);

效率:因為執行緒安全的問題,hashmap 要比 hashtable 效率高一點。另外,hashtable 基本被淘汰,不要在**中使用它;

對null key 和null value的支援:hashmap 中,null 可以作為鍵,這樣的鍵只有乙個,可以有乙個或多個鍵所對應的值為 null。。但是在 hashtable 中 put 進的鍵值只要有乙個 null,直接丟擲 nullpointerexception。

初始容量大小和每次擴充容量大小的不同 :①建立時如果不指定容量初始值,hashtable 預設的初始大小為11,之後每次擴充,容量變為原來的2n+1。hashmap 預設的初始化大小為16。之後每次擴充,容量變為原來的2倍。②建立時如果給定了容量初始值,那麼 hashtable 會直接使用你給定的大小,而 hashmap 會將其擴充為2的冪次方大小。也就是說 hashmap 總是使用2的冪次方作為雜湊表的大小,後面會介紹到為什麼是2的冪次方。

底層資料結構:jdk1.8 以後的 hashmap 在解決雜湊衝突時有了較大的變化,當鍊表長度大於閾值(預設為8)時,將鍊錶轉化為紅黑樹,以減少搜尋時間。hashtable 沒有這樣的機制。

為了能讓 hashmap 訪問高效,盡量較少碰撞,也就是要盡量把資料分配均勻,每個鍊錶/紅黑樹長度大致相同。這個實現就是把資料存到哪個鍊錶/紅黑樹中的演算法。

這個演算法應該如何設計呢?

我們首先可能會想到採用%取餘的操作來實現。但是,重點來了:「取餘(%)操作中如果除數是2的冪次則等價於與其除數減一的與(&)操作(也就是說 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。」並且採用二進位制位操作 &,相對於%能夠提高運算效率,這就解釋了 hashmap 的長度為什麼是2的冪次方。

concurrenthashmap 和 hashtable 的區別主要體現在實現執行緒安全的方式上不同。

兩者的對比圖:

hashtable:

jdk1.7的concurrenthashmap:

首先將資料分為一段一段的儲存,然後給每一段資料配一把鎖,當乙個執行緒占用鎖訪問其中乙個段資料時,其他段的資料也能被其他執行緒訪問。

concurrenthashmap 是由 segment 陣列結構和 hahentry 陣列結構組成

segment 實現了 reentrantlock,所以 segment 是一種可重入鎖,扮演鎖的角色。hashentry 用於儲存鍵值對資料。

static class segmentextends reentrantlock implements serializable乙個 concurrenthashmap 裡包含乙個 segment 陣列。segment 的結構和hashmap類似,是一種陣列和鍊錶結構,乙個 segment 包含乙個 hashentry 陣列,每個 hashentry 是乙個鍊錶結構的元素,每個 segment 守護著乙個hashentry陣列裡的元素,當對 hashentry 陣列的資料進行修改時,必須首先獲得對應的 segment的鎖。

concurrenthashmap取消了segment分段鎖,採用cas和synchronized來保證併發安全。資料結構跟hashmap1.8的結構類似,陣列+鍊錶/紅黑二叉樹。

synchronized只鎖定當前鍊錶或紅黑二叉樹的首節點,這樣只要hash不衝突,就不會產生併發,效率又提公升n倍。

1. list

2. set

這幾道Java集合框架面試題在面試中幾乎必問

arraylist 與 linkedlist 異同 arraylist 與 vector 區別 hashmap的底層實現 hashmap 和 hashtable 的區別 hashmap 的長度為什麼是2的冪次方 hashset 和 hashmap 區別 concurrenthashmap 和 has...

集合框架面試題

arraylist和linkedlist的大致區別 1.arraylist是實現了基於動態陣列的資料結構,linkedlist是基於鍊錶結構。2.對於隨機訪問的get和set方法,arraylist要優於linkedlist,因為linkedlist要移動指標。3.對於新增和刪除操作add和remo...

幾道面試題

系統呼叫與函式的區別 從程式完成的功能來看,函式庫提供的函式通常是不需要作業系統的服務,函式是在使用者空間內執行的,除非函式涉及到i o操作等,一般是不會切到核心態的。系統呼叫是要求作業系統為使用者提供程序,提供某種服務,通常是涉及系統的硬體資源和一些敏感的軟體資源等。函式庫的函式,尤其與輸入輸出相...