Java集合面試總結(題目來源JavaGuide)

問題1:說說 List,Set,Map 三者的區別?

在 Java 中,ListSetMap 是最常用的集合框架(Collection Framework)接口,它們的主要區別如下:

1. List(列表)

  • 特點

    • 允許存儲 重復元素
    • 保持插入順序,即元素的存儲順序與遍歷順序一致。
    • 允許null值(可存多個)。
    • 提供索引訪問,可以使用索引(get(int index))獲取元素。
  • 常見實現類

    • ArrayList(基于數組,查詢快,增刪慢)
    • LinkedList(基于雙向鏈表,查詢慢,增刪快)
    • Vector(線程安全的 ArrayList,但性能較低)
  • 使用場景

    • 需要有序存儲元素,并且可能有重復值,例如存儲一系列日志記錄、待辦事項等。

2. Set(集合)

  • 特點

    • 不允許存儲重復元素
    • 無法通過索引訪問元素(沒有 get(int index) 方法)。
    • 默認情況下不保證元素的存儲順序TreeSet 除外)。
    • 允許最多一個 null 值HashSet 允許,TreeSet 不允許)。
  • 常見實現類

    • HashSet(基于哈希表,存取快,但無序)
    • LinkedHashSet有序的 HashSet,按插入順序存儲)
    • TreeSet(基于紅黑樹自動排序,不允許 null
  • 使用場景

    • 需要存儲唯一的元素集合,例如存儲用戶ID、標簽等。

3. Map(映射)

  • 特點

    • 鍵值對 (key-value) 形式存儲數據。
    • Key 不可重復,但 Value 可以重復。
    • 允許最多一個 null 鍵,多值 null 值(TreeMap 不允許 null 鍵)。
  • 常見實現類

    • HashMap無序,基于哈希表,存取快,線程不安全)
    • LinkedHashMap有序的 HashMap,按插入順序存儲)
    • TreeMap排序的 Map,基于紅黑樹,按 Key 排序)
  • 使用場景

    • 需要存儲鍵值映射,如學生學號-姓名、配置項鍵值對等。

總結對比

特性ListSetMap
是否允許重復元素允許不允許Key 不允許重復,Value 允許
是否保證順序保證插入順序HashSet 無序,LinkedHashSet 保證插入順序,TreeSet 排序HashMap 無序,LinkedHashMap 保持插入順序,TreeMap 按 key 排序
是否可存 null允許多個HashSet 允許一個HashMap 允許一個 null Key,多 null Value,TreeMap 不允許 null Key
訪問方式通過索引 (get(int index))迭代器 (Iterator)通過 Key (get(K key))
適用場景需要有序存儲并允許重復,如購物車、任務列表需要唯一元素,如用戶ID需要鍵值映射,如用戶信息

簡單記憶

  • List 適合 有序、可重復 場景。
  • Set 適合 唯一、無序(或排序) 場景。
  • Map 適合 鍵值映射 場景。

問題2:?List,Set,Map 在 Java 中分別由哪些對應的實現類?底層的數據結構?

在 Java 中,ListSetMap 各自有多個常見的實現類,它們的底層數據結構不同,適用于不同的場景。下面詳細分析各自的實現類及其底層結構。


1. List(列表)

List 主要有以下幾個實現類:

(1)ArrayList

  • 底層數據結構動態數組

  • 特點

    • 查詢速度快(基于數組,通過索引訪問,O(1))。

    • 插入和刪除慢(涉及數據移動,O(n))。

    • 允許 null 元素,允許重復元素。

    • 線程不安全。

  • 適用場景

    • 讀操作多,寫操作少的場景,如緩存數據、搜索結果等。


(2)LinkedList

  • 底層數據結構雙向鏈表

  • 特點

    • 查詢速度慢(鏈表遍歷,O(n))。

    • 插入、刪除速度快(O(1),不涉及數據移動)。

    • 允許 null 元素,允許重復元素。

    • 線程不安全。

  • 適用場景

    • 頻繁插入、刪除元素,如任務隊列、消息隊列等。


(3)Vector

  • 底層數據結構動態數組

  • 特點

    • ArrayList 類似,但線程安全(方法加了 synchronized)。

    • 查詢快,插入刪除慢。

    • 允許 null 元素,允許重復元素。

  • 適用場景

    • 多線程環境下,需要線程安全List


2. Set(集合)

Set 主要有以下幾個實現類:

(1)HashSet

  • 底層數據結構哈希表(基于 HashMap 實現,值存 HashMap 的 key)

  • 特點

    • 無序存儲元素(元素順序可能變化)。

    • 不允許重復元素

    • 允許 null 值(但只能有一個)。

    • 查詢、插入、刪除速度快(平均 O(1))。

  • 適用場景

    • 需要去重的集合,如存儲唯一用戶ID。


(2)LinkedHashSet

  • 底層數據結構哈希表 + 雙向鏈表

  • 特點

    • 有序(按照插入順序存儲)。

    • 不允許重復元素

    • 查詢、插入、刪除速度比 HashSet 略慢(受鏈表影響)。

  • 適用場景

    • 既需要去重,又要保持插入順序的場景,如LRU緩存。


(3)TreeSet

  • 底層數據結構紅黑樹(自平衡二叉搜索樹)

  • 特點

    • 自動排序(默認按升序排序,可自定義 Comparator)。

    • 不允許重復元素

    • 不允許 null

    • 查詢、插入、刪除速度 O(log n)(比 HashSet 慢)。

  • 適用場景

    • 需要排序且去重的集合,如排行榜、時間排序數據。


3. Map(映射)

Map 主要有以下幾個實現類:

(1)HashMap

  • 底層數據結構數組 + 單鏈表 + 紅黑樹(JDK 1.8 之后)

  • 特點

    • Key 無序存儲,不允許重復,允許 null(最多一個)。

    • Value 允許重復,允許 null

    • 查詢、插入、刪除快(O(1),最壞 O(log n))。

    • 線程不安全。

  • 適用場景

    • 需要快速查找 key-value 數據,如緩存、配置項存儲。


(2)LinkedHashMap

  • 底層數據結構哈希表 + 雙向鏈表

  • 特點

    • 按插入順序存儲(可以用 accessOrder=true 變成 LRU 訪問順序)。

    • 查詢、插入、刪除速度略低于 HashMap(受鏈表影響)。

  • 適用場景

    • 需要按插入順序遍歷的 Map,如緩存實現。


(3)TreeMap

  • 底層數據結構紅黑樹

  • 特點

    • Key 自動排序(默認升序,可自定義 Comparator)。

    • Key 不允許 null(Value 允許)。

    • 查詢、插入、刪除 O(log n)

  • 適用場景

    • 需要按 key 排序Map,如時間戳排序的日志數據。


(4)Hashtable

  • 底層數據結構哈希表

  • 特點

    • 線程安全(方法加 synchronized)。

    • 不允許 null key 和 null value

    • 查詢、插入、刪除 O(1)

    • 效率較低(同步開銷大)。

  • 適用場景

    • 需要線程安全Map(但通常用 ConcurrentHashMap 替代)。


(5)ConcurrentHashMap

  • 底層數據結構分段鎖 + 哈希表(JDK 1.7),CAS + 哈希表 + 紅黑樹(JDK 1.8)

  • 特點

    • 線程安全,高效(分段鎖 / CAS 機制)。

    • 不允許 null key 和 null value

    • 查詢、插入、刪除比 Hashtable 快(O(1))。

  • 適用場景

    • 高并發環境下的 Map,如緩存存儲。


總結

類型

實現類

底層數據結構

主要特點

適用場景

List

ArrayList

動態數組

查詢快,增刪慢

讀多寫少

LinkedList

雙向鏈表

插入/刪除快,查詢慢

頻繁增刪

Vector

動態數組

線程安全

多線程

Set

HashSet

哈希表

無序,不重復

唯一集合

LinkedHashSet

哈希表 + 雙向鏈表

插入順序

唯一且有序

TreeSet

紅黑樹

自動排序

唯一且排序

Map

HashMap

數組 + 鏈表 + 紅黑樹

無序,查詢快

快速查找

LinkedHashMap

哈希表 + 雙向鏈表

按插入順序

LRU緩存

TreeMap

紅黑樹

Key 自動排序

需要排序

Hashtable

哈希表

線程安全,低效

線程安全(少用)

ConcurrentHashMap

哈希表 + CAS

高并發

并發環境

?問題3:有哪些集合是線程不安全的?怎么解決呢?

1. 線程不安全的集合

(1)ArrayList(線程不安全)

  • 底層數據結構:動態數組

  • 問題:多線程環境下,多個線程同時 add() 可能導致 數據覆蓋、數組擴容時的數據丟失

  • 解決方案

    1. 使用 Vector(線程安全,但性能較低)

    2. 使用 Collections.synchronizedList(new ArrayList<>())

    3. 使用 CopyOnWriteArrayList(適用于讀多寫少的場景)


(2)HashMap(線程不安全)

  • 底層數據結構:數組 + 鏈表(JDK 1.7),數組 + 鏈表 + 紅黑樹(JDK 1.8)

  • 問題

    • JDK 1.7 多線程 put() 時可能引發死循環(rehash 過程中鏈表反轉)。

    • JDK 1.8 仍然是線程不安全的,多線程 put() 可能會丟數據。

  • 解決方案

    1. 使用 ConcurrentHashMap(高性能并發 Map,推薦)

    2. 使用 Collections.synchronizedMap(new HashMap<>())(性能較低)

    3. 使用 Hashtable(線程安全但性能差,較少使用)


2. ArrayList vs. Vector(高頻對比)

對比項

ArrayList

Vector

線程安全

? 非線程安全

? 線程安全(synchronized)

底層數據結構

動態數組

動態數組

擴容方式

1.5x 擴容

2x 擴容

性能

高(無鎖)

低(同步開銷大)

適用場景

單線程

多線程(但 CopyOnWriteArrayList 更推薦)

💡 為什么 Vector 不推薦?

  • Vector 的方法使用 synchronized 關鍵字,導致所有方法都串行執行,性能較低。

  • 多線程環境下,更推薦 CopyOnWriteArrayListCollections.synchronizedList(new ArrayList<>())


3. HashMap vs. ConcurrentHashMap(高頻對比)

對比項

HashMap

ConcurrentHashMap

線程安全

? 非線程安全

? 線程安全

底層數據結構

數組 + 鏈表(JDK 1.7)
數組 + 鏈表 + 紅黑樹(JDK 1.8)

JDK 1.7分段鎖 + 哈希表(Segment 機制)
JDK 1.8CAS + synchronized + 紅黑樹

null Key / Value

? 允許 null Key 和 null Value

? 不允許 null Key 和 null Value

并發性能

低(多線程可能出錯)

高(JDK 1.8 采用 CAS 機制,提高并發性能)

適用場景

單線程

高并發環境


4. ConcurrentHashMap 如何保證線程安全?

🔹 JDK 1.7(Segment 分段鎖)

  • ConcurrentHashMap 采用 Segment + HashEntry 結構,將整個 Map 分成多個 Segment(默認 16 個)。

  • 每個 Segment 維護自己的鎖,只有訪問相同 Segment 的線程才會競爭鎖,大大提高并發性。

  • 缺點Segment 結構較復雜,鎖的粒度仍然較大。


🔹 JDK 1.8(CAS + synchronized + 紅黑樹)

  • 取消了 Segment,改為 數組 + 鏈表 + 紅黑樹 結構,與 HashMap 結構類似。

  • 核心優化

    1. CAS(Compare-And-Swap)機制:插入新元素時,先嘗試用 CAS 方式寫入,失敗再加鎖。

    2. synchronized 代替 ReentrantLock:JDK 1.8 采用 Node 級別加鎖,僅在 Hash 沖突時加鎖,降低鎖競爭。

    3. 紅黑樹優化:當鏈表長度超過 8 時,轉換為紅黑樹,提高查詢性能。

💡 為什么 ConcurrentHashMap 性能更好?

  • HashTable全表鎖,每次訪問都需要 synchronized,性能低。

  • ConcurrentHashMap 采用 CAS + 局部鎖,大幅提升并發性能。


5. 線程安全的解決方案(匯總)

線程不安全集合

線程安全替代方案

ArrayList

CopyOnWriteArrayList / Collections.synchronizedList(new ArrayList<>())

HashSet

Collections.synchronizedSet(new HashSet<>())

HashMap

ConcurrentHashMap / Collections.synchronizedMap(new HashMap<>())

LinkedList

BlockingQueue(如 LinkedBlockingQueue


6. 總結

  1. ArrayList vs. Vector

    • ArrayList 非線程安全,性能高。

    • Vector 線程安全,但同步開銷大,不推薦。

    • 推薦CopyOnWriteArrayList(讀多寫少)。

  2. HashMap vs. ConcurrentHashMap

    • HashMap 非線程安全,多線程環境下可能丟數據。

    • ConcurrentHashMap 采用 CAS + synchronized 保證線程安全,高并發推薦。

  3. ConcurrentHashMap 如何保證線程安全?

    • JDK 1.7Segment 分段鎖(16 個小鎖)。

    • JDK 1.8CAS + synchronized` + 紅黑樹(更高效)。

如果你在面試中被問到: 👉 ArrayList 和 Vector 的區別?

  • Vector 是同步的,ArrayList 不是,性能上 ArrayList 更好。

  • 現在不推薦 Vector,而是使用 CopyOnWriteArrayList

👉 HashMap 和 ConcurrentHashMap 的區別?

  • HashMap 非線程安全ConcurrentHashMap 線程安全,高并發推薦。

  • JDK 1.8 ConcurrentHashMap 采用 CAS + synchronized,比 JDK 1.7 更高效。

問題4:?HashMap 查詢,刪除的時間復雜度

1. HashMap 底層數據結構(JDK 1.8 之后)

HashMap 采用 數組 + 鏈表 + 紅黑樹 作為底層數據結構:

  • 當沒有哈希沖突時(即均勻分布的情況下):時間復雜度為 O(1)

  • 當發生哈希沖突時

    • 鏈表存儲:查找或刪除需要遍歷鏈表,最壞時間復雜度 O(n)

    • 紅黑樹存儲(鏈表長度 ≥ 8 時轉換為紅黑樹):查詢、刪除的時間復雜度降為 O(log n)


2. HashMap 查詢的時間復雜度

  • 理想情況下(無哈希沖突)

    • 通過 key 計算 hash 值,找到數組索引,直接返回值,時間復雜度 O(1)

  • 最壞情況(所有 key 哈希沖突,存儲為鏈表)

    • 需要遍歷整個鏈表,時間復雜度 O(n)

  • 優化后(鏈表轉紅黑樹)

    • 當鏈表長度 >= 8 時,自動轉為紅黑樹,查詢復雜度降為 O(log n)

? 最終查詢時間復雜度:

  • 平均情況:O(1)

  • 最壞情況(鏈表):O(n)

  • 最壞情況(紅黑樹):O(log n)

示例:

Map<Integer, String> map = new HashMap<>();
map.put(1, "A");
map.put(2, "B");
String value = map.get(1);  // O(1)(理想情況)

3. HashMap 刪除的時間復雜度

  • 理想情況下(無哈希沖突)

    • remove(key) 計算 hash,直接刪除,時間復雜度 O(1)

  • 最壞情況(哈希沖突,鏈表存儲)

    • 需要遍歷鏈表找到 key 并刪除,時間復雜度 O(n)

  • 優化后(鏈表轉紅黑樹)

    • 刪除操作 O(log n)

? 最終刪除時間復雜度:

  • 平均情況:O(1)

  • 最壞情況(鏈表):O(n)

  • 最壞情況(紅黑樹):O(log n)

示例:

map.remove(1);  // O(1)(理想情況)

4. 復雜度總結

操作

平均時間復雜度

最壞時間復雜度(鏈表)

最壞時間復雜度(紅黑樹)

get(key) 查詢

O(1)

O(n)

O(log n)

remove(key) 刪除

O(1)

O(n)

O(log n)

🔹 面試重點:

  • JDK 1.8 之前:查詢、刪除最壞情況 O(n)(哈希沖突形成鏈表)。

  • JDK 1.8 之后:鏈表長度 ≥ 8 時轉為紅黑樹,優化為 O(log n)

問題5:?HashMap 的底層實現

JDK1.8 之前 : 數組和鏈表

JDK1.8 之后 : 多了紅黑樹

問題6:?HashMap 的長度為什么是 2 的冪次方?

問題答案
為什么 HashMap 長度是 2 的冪?為了 優化索引計算,減少哈希沖突
如何計算索引?index = hash & (table.length - 1)
為什么用 & (length - 1) 而不是 % length位運算比取模運算更快
如果 length 不是 2 的冪,會怎樣?索引分布不均,哈希沖突增加,性能下降
HashMap 如何保證 length 是 2 的冪?tableSizeFor(cap) 方法,自動調整 capacity

問題7:?比較 HashSet、LinkedHashSet 和 TreeSet 三者的異同

1. 共同點(相同點)

? 1.1. 都實現了 Set 接口

  • 不允許存儲重復元素,如果嘗試添加相同元素,新的數據會被忽略。

? 1.2. 允許 null

  • HashSetLinkedHashSet 允許存儲一個 null 值。

  • TreeSet 不允許 null(因為 TreeSet 依賴 compareTo() 進行排序,null 無法比較)。

? 1.3. 不是線程安全的

  • 三者都不是線程安全的,若在多線程環境下使用,需要使用 Collections.synchronizedSet()ConcurrentSkipListSet

Set<Integer> set = Collections.synchronizedSet(new HashSet<>());

? 1.4. 不能通過索引訪問元素

  • Set 沒有 get(int index) 方法,不能像 List 那樣通過索引訪問。


2. 不同點(區別)

對比項

HashSet

LinkedHashSet

TreeSet

底層數據結構

哈希表(HashMap

哈希表 + 雙向鏈表

紅黑樹(TreeMap

元素存儲順序

無序

按照插入順序

自動排序(默認升序,可自定義)

是否排序

? 不排序

? 不排序(但保持插入順序)

? 排序(按 compareTo() 規則)

允許 null

? 允許 1 個 null

? 允許 1 個 null

? 不允許 null

查詢性能

🚀 O(1)(哈希存儲,最快)

🚀 O(1)(哈希存儲)

🐌 O(log n)(紅黑樹,較慢)

插入性能

🚀 O(1)(哈希存儲)

🚀 O(1)(哈希存儲)

🐌 O(log n)(紅黑樹,較慢)

刪除性能

🚀 O(1)

🚀 O(1)

🐌 O(log n)

適用場景

需要快速查找的集合

需要保留插入順序的集合

需要排序的集合


3. 三者的底層實現

(1)HashSet

  • 底層使用 HashMapSet 的值存儲在 HashMap 的 key 中,所有 value 設為 Object 類型的固定值

  • 元素無序存儲,無法保證插入順序。

public HashSet() {map = new HashMap<>();
}
public boolean add(E e) {return map.put(e, PRESENT) == null;
}

? 適用場景:需要快速去重,不關心元素順序


(2)LinkedHashSet

  • 底層使用 LinkedHashMap,通過雙向鏈表維護插入順序

  • 保證遍歷順序與插入順序一致

    public LinkedHashSet() {super(16, .75f, true); // 繼承 LinkedHashMap
    }
    

? 適用場景去重但保持插入順序,如 LRU 緩存。


(3)TreeSet

  • 底層使用 TreeMap,基于紅黑樹存儲

  • compareTo() 結果自動排序,默認升序。

  • 不允許 null,因為 null 無法參與比較。

    public TreeSet() {this(new TreeMap<>());
    }
    

? 適用場景需要排序的集合,如排行榜、時間排序的日志


4. 示例代碼

(1)HashSet

Set<String> hashSet = new HashSet<>();
hashSet.add("B");
hashSet.add("A");
hashSet.add("C");
System.out.println(hashSet); // 輸出無序:[A, C, B](順序可能不同)

(2)LinkedHashSet

Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("B");
linkedHashSet.add("A");
linkedHashSet.add("C");
System.out.println(linkedHashSet); // 輸出順序:["B", "A", "C"]

(3)TreeSet

Set<String> treeSet = new TreeSet<>();
treeSet.add("B");
treeSet.add("A");
treeSet.add("C");
System.out.println(treeSet); // 輸出順序:["A", "B", "C"](自動排序)

5. 選擇建議(如何選擇?)

場景

推薦使用

需要去重,不關心順序,查詢性能最高

HashSet(最快)

需要去重,且保持插入順序

LinkedHashSet

需要去重,且按大小排序

TreeSet


6. 重點面試問題

💡 1. HashSet、LinkedHashSet、TreeSet 的區別?

  • HashSet:基于 HashMap,無序存儲,O(1) 查詢

  • LinkedHashSet:基于 LinkedHashMap,有序存儲

  • TreeSet:基于 TreeMap,自動排序,O(log n) 查詢

💡 2. HashSet 為什么查詢快?

  • HashSet 基于 HashMap,使用 hashCode() 計算索引,查詢平均 O(1)

💡 3. TreeSet 為什么不允許 null

  • TreeSet 依賴 compareTo() 進行排序,而 null 不能比較。

💡 4. TreeSet 是如何排序的?

  • 通過 Comparable 接口 (compareTo()) 或 Comparator 自定義排序規則。


7. 總結

Set 類型

底層數據結構

順序

查詢性能

插入/刪除性能

是否排序

HashSet

哈希表(HashMap

無序

🚀 O(1)

🚀 O(1)

?

LinkedHashSet

哈希表 + 雙向鏈表(LinkedHashMap

插入順序

🚀 O(1)

🚀 O(1)

?

TreeSet

紅黑樹(TreeMap

排序

🐌 O(log n)

🐌 O(log n)

?

🔹 總結

  • HashSet 最快,但無序。

  • LinkedHashSet 有序,比 HashSet 稍慢。

  • TreeSet 自動排序,但性能最慢(O(log n))。

問題8:?HashMap 和 Hashtable 的區別?HashMap 和 HashSet 區別?HashMap 和 TreeMap 區別? ConcurrentHashMap 和 Hashtable 的區別?

1. HashMap vs. Hashtable(區別)

對比項

HashMap

Hashtable

線程安全

? 非線程安全

? 線程安全(方法使用 synchronized 修飾)

底層數據結構

數組 + 鏈表(JDK 1.7)
數組 + 鏈表 + 紅黑樹(JDK 1.8)

數組 + 鏈表

性能

🚀 高(無鎖)

🐌 低(全表鎖)

null 是否允許

? 允許 null key 和 null value

? 不允許 null key 和 null value

擴容方式

capacity * 2(翻倍擴容)

capacity * 2 + 1

適用場景

單線程或高并發(推薦使用 ConcurrentHashMap

低并發場景(通常不推薦)

? 總結:

  • HashMap 性能更高,適用于 單線程或并發環境(推薦 ConcurrentHashMap

  • Hashtable 所有方法加鎖,線程安全但性能低,一般不推薦使用。

💡 面試問法:

  • 為什么 Hashtable 不推薦使用?

    • Hashtable 全表鎖,并發效率低,通常使用 ConcurrentHashMap 替代。


2. HashMap vs. HashSet(區別)

對比項

HashMap

HashSet

實現接口

Map<K, V>

Set<E>

數據存儲方式

key-value 形式

僅存 key,值始終為 new Object()

底層數據結構

數組 + 鏈表 + 紅黑樹(JDK 1.8)

基于 HashMapvalue 是固定對象

是否允許重復

? key 唯一value 可重復

? 不允許重復元素

null 是否允許

? key 允許一個 nullvalue 可多個 null

? 允許一個 null

查詢性能

🚀 O(1)

🚀 O(1)

適用場景

需要存儲鍵值對(如用戶 ID -> 用戶信息)

需要存儲唯一元素集合(如用戶 ID 集合)

? 總結:

  • HashMap 存儲鍵值對HashSet 僅存儲 key(基于 HashMap 實現)。

  • HashSet 去重能力基于 HashMapkey 唯一性

💡 面試問法:

  • HashSet 為什么是基于 HashMap 實現的?

    • HashSet 本質上就是 HashMapvalue 始終存一個固定對象。

public boolean add(E e) {return map.put(e, PRESENT) == null;
}

3. HashMap vs. TreeMap(區別)

對比項

HashMap

TreeMap

底層數據結構

數組 + 鏈表 + 紅黑樹

紅黑樹(TreeMap

排序方式

? 無序

? 自動排序(默認升序)

查詢性能

🚀 O(1)(無沖突)

🐌 O(log n)(紅黑樹)

null 是否允許

? key 允許 null

? key 不允許 null

適用場景

需要快速查找的鍵值對

需要排序的鍵值對(如排行榜)

? 總結:

  • HashMap 查詢快(O(1))無序,適合快速存取數據

  • TreeMap 自動排序(O(log n)),適用于需要排序的場景

💡 面試問法:

  • 如果 HashMap 需要排序怎么辦?

    • 使用 TreeMap 或者 LinkedHashMap

4. ConcurrentHashMap vs. Hashtable(區別)

對比項

ConcurrentHashMap(推薦)

Hashtable(已淘汰)

底層數據結構

JDK 1.7:分段鎖(Segment
JDK 1.8:CAS + synchronized(紅黑樹)

哈希表 + 全表鎖

鎖的粒度

局部鎖(高并發)

全表鎖(低效)

查詢性能

🚀 高(讀 O(1),寫 O(1) ~ O(log n))

🐌 低(所有方法 synchronized

null 是否允許

? 不允許 null key/value

? 不允許 null key/value

適用場景

高并發環境(推薦)

低并發環境(已淘汰)

? 總結:

  • ConcurrentHashMap 替代 Hashtable,采用 CAS + 局部鎖,更高效。

  • Hashtable 線程安全但性能低,一般不再使用。

💡 面試問法:

  • ConcurrentHashMap 如何保證線程安全?

    • JDK 1.7Segment 分段鎖(16 個小鎖,提高并發性能)。

    • JDK 1.8CAS + synchronized` + 紅黑樹(更高效)。


5. 總結

對比項

HashMap vs. Hashtable

HashMap vs. HashSet

HashMap vs. TreeMap

ConcurrentHashMap vs. Hashtable

線程安全

? HashMap 非線程安全,? Hashtable 線程安全(但慢)

HashMap 存 key-value,HashSet 只存 key

HashMap 無序,TreeMap 排序

ConcurrentHashMap 更高效(局部鎖),Hashtable 全表鎖(淘汰)

底層結構

HashMap:哈希表,Hashtable:哈希表(帶鎖)

HashSet 基于 HashMap

HashMap 哈希表,TreeMap 紅黑樹

ConcurrentHashMap 分段鎖(JDK 1.7) / CAS + synchronized(JDK 1.8)

查詢性能

🚀 O(1) vs. 🐌 O(1)(但有鎖)

🚀 O(1) vs. 🚀 O(1)

🚀 O(1) vs. 🐌 O(log n)

🚀 O(1) vs. 🐌 O(1)(但有鎖)

是否排序

? 都無序

? 都無序

? HashMap 無序,? TreeMap 排序

? 都無序

null 允許性

HashMap 允許 null key/value,Hashtable 不允許 null

? HashSet 允許一個 null

? TreeMap 不允許 null key

? ConcurrentHashMap 不允許 null

🚀 面試重點:

  • 高并發環境:ConcurrentHashMap 代替 Hashtable

  • 需要排序:用 TreeMap

  • 去重:用 HashSet(基于 HashMap

?問題9:ConcurrentHashMap 線程安全的具體實現方式/底層具體實現

1. JDK 1.7 中的實現(分段鎖)

在 JDK 1.7 中,ConcurrentHashMap 采用了 分段鎖(Segment Locking) 機制。HashMap 的每個**桶(Segment)**都是獨立加鎖的,允許多個線程并發操作不同的段,但同一段內的操作需要加鎖。

1.1. 分段鎖模型

  • ConcurrentHashMap 會把整個 map 劃分為多個 段(Segment),每個段內部維護一個 HashMap,并且每個段有一個鎖。

  • 每個段都可以并發操作,不同段之間的鎖是獨立的,這就避免了全表鎖定,從而大大提高了并發性能。

  • 每個段的默認大小是 16(即 16 個桶),并且段數可以動態調整。

1.2. 鎖粒度

  • 鎖定段: 每次只能鎖住某個段(而不是整個 ConcurrentHashMap)。

  • 不同段的并發訪問: 不同線程可以并發地對不同段進行 getput 等操作。

  • 同一段的并發訪問: 如果多個線程訪問同一段的不同元素,則這些線程會被阻塞,直到持有該段鎖的線程釋放鎖。

1.3. 分段鎖的代碼示例

public class ConcurrentHashMap<K, V> {static final int MAX_SEGMENT = 16; // 默認分成16個段final Segment<K, V>[] segments;// Segment 內部結構static final class Segment<K, V> extends ReentrantLock implements Serializable {transient volatile HashEntry<K, V>[] table;transient volatile int count;final float loadFactor;Segment(int initialCapacity, float loadFactor) {this.loadFactor = loadFactor;// Initialize the segment's hash table with the given capacity}// Segment 內部的操作方法(put、get 等)public V get(Object key) {// 獲取 key 對應的 value}public void put(K key, V value) {// 將 key-value 對存入當前段的 table 中}}// 主 Map 操作,基于多個 Segment 來完成public V get(Object key) {int hash = key.hashCode();Segment<K, V> segment = segmentFor(hash);return segment.get(key);}public void put(K key, V value) {int hash = key.hashCode();Segment<K, V> segment = segmentFor(hash);segment.put(key, value);}private Segment<K, V> segmentFor(int hash) {return segments[(hash >>> 16) & (segments.length - 1)];}
}

1.4. 分段鎖模型的優缺點

優點:

  • 細粒度鎖:只鎖住某一段,多個線程可以并行操作不同的段,并行度較高。

  • 線程安全:每個段是獨立加鎖的,避免了全表加鎖帶來的性能瓶頸。

缺點:

  • 內存占用高:每個段內部都是一個完整的 HashMap,在大數據量時可能會占用較多內存。

  • 鎖競爭:如果多個線程訪問同一段,仍然會發生阻塞。

2. JDK 1.8 中的實現(CAS + synchronized

在 JDK 1.8 中,ConcurrentHashMap 做了重大優化,去除了分段鎖,改用 CAS(Compare-And-Swap)synchronized 來實現線程安全。其底層不再使用多個段,而是直接將數據存儲在一個 桶數組(數組 + 鏈表 + 紅黑樹) 中,通過 分段鎖和局部鎖 的方式來提供高并發性。

2.1. CAS 和 synchronized 的組合

  • CASConcurrentHashMap 采用 樂觀鎖,在更新數據時首先通過 CAS 操作嘗試更新,如果失敗(例如并發更新),則進行重試。

  • synchronized:對于需要一致性的操作(如擴容),采用 synchronized 鎖定局部區域(如某個桶),保證數據一致性。

2.2. JDK 1.8 的底層結構

  1. 桶數組:和 HashMap 類似,ConcurrentHashMap 內部使用數組來存儲鍵值對。每個桶可以是 鏈表(哈希沖突時)或 紅黑樹(當鏈表過長時轉換為紅黑樹)。

  2. 每個桶的操作

    • 使用 synchronized 鎖定某個桶的插入、刪除、查找操作。

    • 對于普通操作(如插入、查詢),采用 CAS 來保證數據的一致性。

  3. 擴容:當負載因子超過一定閾值時,ConcurrentHashMap 會進行擴容(桶數翻倍),這時需要用 synchronized 來保護擴容過程。

2.3. JDK 1.8 代碼示例

public class ConcurrentHashMap<K, V> {// 存儲數據的桶數組transient volatile Node<K, V>[] table;private final float loadFactor = 0.75f;private transient volatile int sizeCtl;// 鎖定某個桶public V get(Object key) {int hash = hash(key);Node<K, V> e;if ((e = table[hash & (table.length - 1)]) != null) {// 通過鏈表查找,若鏈表長度大于閾值轉為紅黑樹synchronized (e) {// 查找并返回}}return null;}// 使用 CAS 更新值public boolean putIfAbsent(K key, V value) {int hash = hash(key);Node<K, V> e;if ((e = table[hash & (table.length - 1)]) != null) {// 使用 CAS 更新,如果失敗則重試}return true;}// 擴容private void resize() {synchronized (this) {// 擴容并重置桶數組}}// hash 計算(保證一致性)private int hash(Object key) {return key.hashCode();}
}

2.4. JDK 1.8 實現的優缺點

優點:

  • 更高效:通過 CAS 和局部鎖提高了并發性能,不需要全表加鎖。

  • 擴展性強:自動擴容時,通過同步鎖保護內存,避免了擴容過程中的數據不一致。

  • 支持高并發:多個線程可以在不同桶上并行操作,極大提高了吞吐量。

缺點:

  • 復雜度高:相比 JDK 1.7,JDK 1.8 的實現更加復雜,使用了 CAS 和 synchronized 等技術,需要處理各種競爭條件。

  • 內存開銷:每個桶存儲的是 Node,這比 HashMap 更占用內存。


3. 線程安全的關鍵點

  • CAS(Compare-And-Swap):樂觀鎖的一種實現方式,通過原子操作檢查和更新數據。如果當前數據符合預期,則修改數據;否則重試。

  • synchronized:用于保護需要保證一致性的關鍵操作(如擴容、遷移)。

  • 紅黑樹優化:當鏈表的長度超過一定閾值(8),HashMap 會將鏈表轉換為紅黑樹,優化查詢性能。


4. 總結

  • JDK 1.7:使用 分段鎖,將 ConcurrentHashMap 劃分為多個段,每個段有一個獨立的鎖,多個線程可以并行操作不同的段。

  • JDK 1.8:采用 CAS + synchronized 技術,使用桶數組(數組 + 鏈表 + 紅黑樹)來存儲數據。通過局部鎖和 CAS 實現高并發。

  • 線程安全性ConcurrentHashMap 通過 細粒度鎖(桶級別、段級別鎖)和 CAS 操作 保證線程安全。

ConcurrentHashMap 是高并發場景下非常常用的工具,在需要線程安全的并發環境下,它是 HashtablesynchronizedMap 的更好替代品。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/68026.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/68026.shtml
英文地址,請注明出處:http://en.pswp.cn/web/68026.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

deepseek接入pycharm 進行AI編程

要將DeepSeek接入PyCharm進行AI編程,可以按照以下步驟操作: ### 1. 獲取DeepSeek API訪問權限 DeepSeek通常以API的形式對外提供服務,你需要在其官方網站注冊賬號,申請API訪問權限。在申請通過后,會獲得API密鑰(API Key),這是后續調用API的關鍵憑證。 ### 2. 安裝必要…

奧迪改名風波再起,A6L能否率隊創下新奇跡

文/王俁祺 導語&#xff1a;春節假期剛過&#xff0c;奧迪的車型命名規則又變了。在如今以內卷為主基調的環境下&#xff0c;車型改名可不是小事&#xff0c;而奧迪的這次調整背后藏著許多深意&#xff0c;也預示著2025年奧迪在產品布局上的新動向。 改名能否“改命” 回溯到…

【怎么用系列】短視頻戒除-1-對推薦算法進行干擾

如今推薦算法已經滲透到人們生活的方方面面&#xff0c;尤其是抖音等短視頻核心就是推薦算法。 【短視頻的危害】 1> 會讓人變笨&#xff0c;慢慢讓人喪失注意力與專注力 2> 讓人喪失閱讀長文的能力 3> 讓人沉浸在一個又一個快感與嗨點當中。當我們刷短視頻時&#x…

改進Transformer,解讀Tokenformer論文:基于參數分詞化重新思考Transformer的擴展策略

Transformer 訓練成本高昂的問題日益凸顯&#xff0c;不僅需要耗費巨額的資金與大量的計算資源&#xff0c;還對環境產生了不可忽視的影響&#xff0c;最近由北京大學與谷歌聯合發表的一篇論文&#xff0c;為這一棘手難題帶來了全新的曙光。論文中提出的創新方案&#xff0c;有…

【STM32】HAL庫USB虛擬U盤MSC配置及采用自帶的Flash作為文件系統

【STM32】HAL庫USB虛擬U盤MSC實現配置及采用自帶的Flash作為文件系統 本文將自帶的Flash作為文件系統 通過配置USB的MSC功能實現虛擬U盤 沒有單獨建立FATFS文件系統 僅僅是配置USB和Flash讀寫而已 當然 這里也可以用外部Flash等等 也可以配置文件系統來進行套殼 但總體而言不如…

Noise Conditional Score Network

NCSN p σ ( x ~ ∣ x ) : N ( x ~ ; x , σ 2 I ) p_\sigma(\tilde{\mathrm{x}}|\mathrm{x}) : \mathcal{N}(\tilde{\mathrm{x}}; \mathrm{x}, \sigma^2\mathbf{I}) pσ?(x~∣x):N(x~;x,σ2I) p σ ( x ~ ) : ∫ p d a t a ( x ) p σ ( x ~ ∣ x ) d x p_\sigma(\mathrm…

jdk8 G1收集器怎么手動調優

在 JDK 8 中&#xff0c;手動調優 G1 垃圾收集器可以通過以下步驟和參數進行&#xff1a; 1. 啟用 G1 垃圾收集器 要啟用 G1 垃圾收集器&#xff0c;需要在 JVM 啟動參數中添加以下選項&#xff1a; -XX:UseG1GC 這個參數告訴 JVM 使用 G1 作為垃圾收集器。 2. 設置堆內存…

Nginx通過設置自定義標記識別代理調用

Nginx通過設置自定義標記識別代理調用 業務場景 最近遇到一個業務場景&#xff0c;部署在云端服務器的一個平臺&#xff0c;接口提供給多個現場調用&#xff0c;其中一個現場是通過nginx代理服務器代理轉發到云服務器&#xff0c;另外一個現場則是直接通過云服務器接口進行調…

前端知識速記:POST和GET

前端知識速記&#xff1a;POST和GET請求的區別 一、GET請求概述 GET請求是一種用于獲取服務器資源的請求方式。**使用GET請求時&#xff0c;數據通過URL傳遞&#xff0c;適合用于獲取數據而不修改資源。**以下是GET請求的一些基本特征&#xff1a; 數據附在URL后面&#xff…

axios如何利用promise無痛刷新token

目錄 需求 需求解析 實現思路 方法一&#xff1a; 方法二&#xff1a; 兩種方法對比 實現 封裝axios基本骨架 instance.interceptors.response.use攔截實現 問題和優化 如何防止多次刷新token 同時發起兩個或以上的請求時&#xff0c;其他接口如何重試 最后完整代…

【DeepSeek系列】01 DeepSeek-V1 快速入門

1、DeepSeek簡介 2024年底&#xff0c;DeepSeek 相繼推出了其第一代推理大模型&#xff1a;DeepSeek-R1-Zero 和 DeepSeek-R1。 DeepSeek-R1-Zero 是一個通過大規模強化學習&#xff08;RL&#xff09;訓練的模型&#xff0c;訓練過程中沒有使用監督微調&#xff08;SFT&…

基于LabVIEW的Modbus-RTU設備通信失敗問題分析與解決

在使用 LabVIEW 通過 Modbus-RTU 協議與工業設備進行通信時&#xff0c;可能遇到無法正常發送或接收指令的問題。常見原因包括協議參數配置錯誤、硬件連接問題、數據幀格式不正確等。本文以某 RGBW 控制器調光失敗為例&#xff0c;提出了一種通用的排查思路&#xff0c;幫助開發…

【初/高中生講機器學習】0. 本專欄 “食用” 指南——寫在一周年之際?

創建時間&#xff1a;2025-01-27 首發時間&#xff1a;2025-01-29 最后編輯時間&#xff1a;2025-01-29 作者&#xff1a;Geeker_LStar 你好呀~這里是 Geeker_LStar 的人工智能學習專欄&#xff0c;很高興遇見你~ 我是 Geeker_LStar&#xff0c;一名高一學生&#xff0c;熱愛計…

密云生活的初體驗

【】在《歲末隨筆之碎碎念》里&#xff0c;我通告了自己搬新家的事情。乙巳年開始&#xff0c;我慢慢與大家分享自己買房裝修以及在新家的居住體驗等情況。 跳過買房裝修的內容&#xff0c;今天先說說這三個月的生活體驗。 【白河】 潮白河是海河水系五大河之一&#xff0c;貫穿…

系統通解:超多視角理解

在科學研究和工程應用中&#xff0c;我們常常面臨各種復雜系統&#xff0c;需要精確描述其行為和變化規律。從物理世界的運動現象&#xff0c;到化學反應的進程&#xff0c;再到材料在受力時的響應&#xff0c;這些系統的行為往往由一系列數學方程來刻畫。通解&#xff0c;正是…

Python爬蟲:1藥城店鋪爬蟲(完整代碼)

??????????歡迎來到我的博客?????????? &#x1f434;作者&#xff1a;秋無之地 &#x1f434;簡介&#xff1a;CSDN爬蟲、后端、大數據領域創作者。目前從事python爬蟲、后端和大數據等相關工作&#xff0c;主要擅長領域有&#xff1a;爬蟲、后端、大數據…

openwebui入門

1 簡介 ?Open WebUI?&#xff08;網址是openwebui.com&#xff09;是一個高度可擴展、功能強大且用戶友好的自托管Web用戶界面&#xff0c;專為完全離線操作設計&#xff0c;編程語言是python。它支持對接Ollama和OpenAI兼容的API的大模型。? Open WebUI?在架構上是一種中…

Day36-【13003】短文,數組的行主序方式,矩陣的壓縮存儲,對稱、三角、稀疏矩陣和三元組線性表,廣義表求長度、深度、表頭、表尾等

文章目錄 本次課程內容第四章 數組、廣義表和串第一節 數組及廣義表數組的基本操作數組的順序存儲方式-借用矩陣行列式概念二維數組C語言對應的函數-通常行主序方式 矩陣的壓縮存儲對稱矩陣和三角矩陣壓縮存儲后&#xff0c;采用不同的映射函數稀疏矩陣-可以構成三元組線性表三…

Android原生開發入門

1. 資源地址 Android官方教程Android參考手冊 2. 必看基礎模塊 應用基礎知識View 綁定 &#xff1a;綁定相當于Qt中的ui文件生成界面代碼的機制&#xff0c;Qt中的ucc會自動將ui文件編譯成ui_xxxx.h文件&#xff0c;Android開發中也一樣。 Android中自動生成的代碼在&#x…

3-Not_only_base/2018網鼎杯

3-Not_only_base 打開code MCJIJSGKPZZYXZXRMUW3YZG3ZZG3HQHCUS 分析&#xff1a; 首先看題知道解密過程中肯定有base解密。 知識點1&#xff1a; Base64字符集&#xff1a; 包含大小寫字母&#xff08;A-Z、a-z&#xff09;、數字&#xff08;0-9&#xff09;以及兩個特殊字…