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:
jdk1.8的concurrenthashmap(treebin: 紅黑二叉樹節點 node: 鍊錶節點):
首先將資料分為一段一段的儲存,然後給每一段資料配一把鎖,當乙個執行緒占用鎖訪問其中乙個段資料時,其他段的資料也能被其他執行緒訪問。
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 ...
集合框架面試題
arraylist和linkedlist的大致區別 1.arraylist是實現了基於動態陣列的資料結構,linkedlist是基於鍊錶結構。2.對於隨機訪問的get和set方法,arraylist要優於linkedlist,因為linkedlist要移動指標。3.對於新增和刪除操作add和remo...
幾道面試題
系統呼叫與函式的區別 從程式完成的功能來看,函式庫提供的函式通常是不需要作業系統的服務,函式是在使用者空間內執行的,除非函式涉及到i o操作等,一般是不會切到核心態的。系統呼叫是要求作業系統為使用者提供程序,提供某種服務,通常是涉及系統的硬體資源和一些敏感的軟體資源等。函式庫的函式,尤其與輸入輸出相...