Java基礎 Map集合框架 LinkedHashMap

LinkedHashMap

  • LinkedHashMap類架構與繼承關系
  • 核心特性
    • 繼承自 HashMap
    • 有序性
      • 插入順序
      • 訪問順序
    • 雙向鏈表結構
    • 非線程安全
      • 1.并發修改導致數據丟失
      • 2.并發迭代導致 ConcurrentModificationException
      • 3.并發修改導致鏈表結構破壞
      • 解決方案
        • 1. 使用 Collections.synchronizedMap(簡單同步)
        • 2. 使用 ConcurrentHashMap(推薦)
        • 3.使用 ConcurrentLinkedHashMap(保持順序)
        • 4. 自定義線程安全LinkedHashMap
    • 性能影響
  • 構造方法
  • 核心操作邏輯(雙向鏈表維護介紹)
    • 插入操作(put)
      • linkNodeLast
      • afterNodeAccess
      • afterNodeInsertion
    • 移除操作(remove)
    • 迭代遍歷操作(Iterator)
  • 常見應用場景
  • 注意事項
  • 總結

LinkedHashMap類架構與繼承關系

集合框架中的LinkedHashMap,它繼承自 HashMap,在HashMap基礎之上通過維護一個雙向鏈表實現了元素的順序存儲。與 HashMap 的無序性不同,LinkedHashMap提供了兩種可預測的迭代順序:插入順序訪問順序

public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>
{

LinkedHashMap的節點特性

    static class Entry<K,V> extends HashMap.Node<K,V> {Entry<K,V> before, after;// 前后指針 表明節點先后指向Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}}

核心特性

繼承自 HashMap

LinkedHashMap 完全擁有 HashMap 的所有能力和特性,包括允許一個null鍵和null

    public static void main(String[] args) {// 1.創建 LinkedHashMap(默認插入順序)Map<String,Integer> linkMap = new LinkedHashMap<>();// 2.添加元素 - 繼承自 HashMap 的 put() 方法linkMap.put("Alice", 90);  // O(1)時間復雜度linkMap.put("Bob", 85);linkMap.put("Charlie", 95);linkMap.put(null, 0);      // 允許 null 鍵linkMap.put("Dave", null); // 允許 null 值// 3.訪問元素 - 繼承自 HashMap 的 get() 方法System.out.println("Bob's score: " + linkMap.get("Bob"));  // 輸出: 85System.out.println("Null key: " + linkMap.get(null));     // 輸出: 0//4. 刪除元素 - 繼承自 HashMap 的 remove() 方法linkMap.remove("Charlie");// 5. 檢查鍵/值是否存在 - 繼承自 HashMap 的方法System.out.println("Contains key 'Alice': " + linkMap.containsKey("Alice")); // trueSystem.out.println("Contains value 100: " + linkMap.containsValue(100));     // false// 6. 替換值 - 繼承自 HashMap 的方法linkMap.replace("Bob", 85, 88);  // 條件替換System.out.println("\nBob's updated score: " + linkMap.get("Bob"));  // 88// 7. 清空和大小檢查System.out.println("Size before clear: " + linkMap.size());  // 4linkMap.clear();System.out.println("Size after clear: " + linkMap.size());   // 0}

存儲和刪除數據也是HashMap的get()/put() 方法,所以在哈希應用上是一樣的
1.使用哈希桶存儲數據,get()/put() 方法操作平均時間復雜度 O(1 )
2.同樣相同的哈希沖突解決機制(鏈表+紅黑樹)

有序性

區別于HashMap的無序性,LinkedHashMap是有序的,分為插入順序和訪問順序排序,構造默認是插入順序,但可指定選擇哪種有序性

HashMap的遍歷方式舉例

    public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("Alice",1);map.put("Bob",2);map.put("Charlie",3);map.put("Dave",4);map.put("EB",5);map.forEach((K,V)->System.out.println(K));System.out.println("\nIteration (insertion order):");for (Map.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}System.out.println("Keys: " + map.keySet());System.out.println("Values: " + map.values());System.out.println("Entries: " + map.entrySet());}
輸出結果: 每次都可能不一樣 并沒有按照元素插入的先后順序 或者 元素的大小排序
Apple
Bob
Charlie
Error
DaveIteration (insertion order):
Apple: 1
Bob: 2
Charlie: 3
Error: 5
Dave: 4
Keys: [Apple, Bob, Charlie, Error, Dave]
Values: [1, 2, 3, 5, 4]
Entries: [Apple=1, Bob=2, Charlie=3, Error=5, Dave=4]

插入順序

(默認):元素按添加到 Map 的順序排列(迭代順序 = 插入順序)
遍歷方法還是繼承自HashMap,但區別在于輸出結果是按照元素的先后插入順序輸出

    public static void main(String[] args) {Map<String,Integer> map = new LinkedHashMap<>();map.put("Apple",1);map.put("Bob",2);map.put("Charlie",3);map.put("Dave",4);map.put("Error",5);map.forEach((K,V)->System.out.println(K));System.out.println("\nIteration (insertion order):");for (Map.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}System.out.println("Keys: " + map.keySet());System.out.println("Values: " + map.values());System.out.println("Entries: " + map.entrySet());}

輸出結果按照元素插入的先后順序排序

Apple
Bob
Charlie
Dave
ErrorIteration (insertion order):
Apple: 1
Bob: 2
Charlie: 3
Dave: 4
Error: 5
Keys: [Apple, Bob, Charlie, Dave, Error]
Values: [1, 2, 3, 4, 5]
Entries: [Apple=1, Bob=2, Charlie=3, Dave=4, Error=5]

訪問順序

元素按最近訪問(get/put)的時間排序(LRU 原則),適合實現緩存
LRU(Least Recently Used)原則: 指“最近最少使用”原則

訪問順序(LRU 模式)

LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true);
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);map.get("A"); // 訪問 A,使其移動到鏈表末尾
map.put("B", 4); // 更新 B,也會移動// 輸出順序:C -> A -> B(最久未訪問的在前)
System.out.println(map.keySet()); // [C, A, B]

雙向鏈表結構

  • 內部額外維護一個雙向鏈表,記錄所有節點的順序(鏈尾插入法)
    在這里插入圖片描述
  • 鏈表頭是最早插入/最久未訪問的元素,鏈表尾是最近插入/訪問的元素(LRU原則)

非線程安全

因為繼承自HashMap,所以安全問題上 除了數據元素安全問題,并且LinkedHashMap維護的雙向鏈表順序也有可能出現問題

1.并發修改導致數據丟失

public class LinkedHashMapConcurrencyDemo {public static void main(String[] args) throws InterruptedException {// 創建非線程安全的 LinkedHashMapMap<Integer, String> map = new LinkedHashMap<>();// 創建線程池ExecutorService executor = Executors.newFixedThreadPool(10);// 并發添加1000個元素for (int i = 0; i < 1000; i++) {final int key = i;executor.submit(() -> {map.put(key, "Value" + key);});}executor.shutdown();executor.awaitTermination(1, TimeUnit.MINUTES);// 檢查結果 - 通常小于1000System.out.println("最終大小: " + map.size()); System.out.println("丟失元素數量: " + (1000 - map.size()));}
}

典型輸出:小于設想中的數量

最終大小: 978
丟失元素數量: 22

2.并發迭代導致 ConcurrentModificationException

public class IterationConcurrencyDemo {public static void main(String[] args) {Map<Integer, Integer> map = new LinkedHashMap<>();// 初始化數據for (int i = 0; i < 100; i++) {map.put(i, i);}// 讀線程Thread reader = new Thread(() -> {try {for (int i = 0; i < 10; i++) {for (Integer key : map.keySet()) {System.out.print(key + " ");Thread.sleep(10);}System.out.println();}} catch (ConcurrentModificationException e) {System.err.println("發生并發修改異常!");} catch (InterruptedException ignored) {}});// 寫線程Thread writer = new Thread(() -> {Random rand = new Random();for (int i = 0; i < 50; i++) {map.put(rand.nextInt(1000), 1);try {Thread.sleep(5);} catch (InterruptedException ignored) {}}});reader.start();writer.start();}
}

典型輸出:

0 發生并發修改異常!

3.并發修改導致鏈表結構破壞

public class StructureDamageDemo {public static void main(String[] args) throws InterruptedException {LinkedHashMap<Integer, String> map = new LinkedHashMap<>();map.put(1, "A");map.put(2, "B");map.put(3, "C");// 線程1:迭代并修改Thread t1 = new Thread(() -> {Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();while (it.hasNext()) {Map.Entry<Integer, String> entry = it.next();if (entry.getKey() == 2) {// 嘗試刪除元素map.remove(entry.getKey());}}});// 線程2:同時添加元素Thread t2 = new Thread(() -> {for (int i = 4; i < 10; i++) {map.put(i, "X" + i);}});t1.start();t2.start();t1.join();t2.join();// 嘗試迭代可能拋出異常或死循環System.out.println("嘗試迭代結果:");for (Integer key : map.keySet()) {System.out.print(key);}}
}

可能結果:

Exception in thread "Thread-0" java.util.ConcurrentModificationExceptionat java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719)at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:752)at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:750)at linkMapDemo.StructureDamageDemo.lambda$main$0(StructureDamageDemo.java:18)at java.lang.Thread.run(Thread.java:748)
嘗試迭代結果:
13456789

解決方案

1. 使用 Collections.synchronizedMap(簡單同步)
// 創建同步包裝的LinkedHashMap
Map<Integer, String> safeMap = Collections.synchronizedMap(new LinkedHashMap<>()
);// 使用示例
synchronized (safeMap) {// 迭代時需要手動同步for (Integer key : safeMap.keySet()) {System.out.println(key + ": " + safeMap.get(key));}
}// 單操作是線程安全的
safeMap.put(1, "A");
safeMap.remove(2);

適用場景:低并發環境,讀寫比例均衡

2. 使用 ConcurrentHashMap(推薦)

注意:ConcurrentHashMap 不保持插入順序

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;// 創建ConcurrentHashMap(不保持插入順序)
ConcurrentMap<Integer, String> concurrentMap = new ConcurrentHashMap<>();// 使用示例 - 完全線程安全
concurrentMap.put(1, "A");
String value = concurrentMap.get(1);// 原子操作示例
concurrentMap.compute(1, (k, v) -> v + "-updated");
3.使用 ConcurrentLinkedHashMap(保持順序)
// Maven依賴: com.github.ben-manes.caffeine:caffeine
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;// 創建線程安全的LRU緩存(保持訪問順序)
Cache<Integer, String> cache = Caffeine.newBuilder().maximumSize(1000).build();// 線程安全操作
cache.put(1, "A");
String value = cache.getIfPresent(1);// 統計信息
System.out.println(cache.stats());

優點:高性能、保持順序、豐富的API

4. 自定義線程安全LinkedHashMap

使用讀寫鎖ReentrantReadWriteLock

import java.util.*;
import java.util.concurrent.locks.*;public class SafeLinkedHashMap<K, V> extends LinkedHashMap<K, V> {private final ReadWriteLock lock = new ReentrantReadWriteLock();private final Lock readLock = lock.readLock();private final Lock writeLock = lock.writeLock();@Overridepublic V put(K key, V value) {writeLock.lock();try {return super.put(key, value);} finally {writeLock.unlock();}}@Overridepublic V get(Object key) {readLock.lock();try {return super.get(key);} finally {readLock.unlock();}}@Overridepublic Set<K> keySet() {readLock.lock();try {return new HashSet<>(super.keySet());} finally {readLock.unlock();}}// 實現其他需要同步的方法...public void iterateSafely(Consumer<Map.Entry<K, V>> action) {writeLock.lock(); // 迭代需要互斥鎖try {for (Map.Entry<K, V> entry : entrySet()) {action.accept(entry);}} finally {writeLock.unlock();}}
}
方案順序保持并發性能實現復雜度適用場景
Collections.synchronizedMap低(全表鎖)簡單低并發,簡單應用
ConcurrentHashMap高(分段鎖)簡單高并發,不要求順序
ConcurrentLinkedHashMap (Caffeine)是(訪問順序)極高中等高并發緩存系統
自定義讀寫鎖實現中高(讀寫分離)復雜需要精確控制的高并發
ConcurrentSkipListMap是(排序順序)簡單需要排序的并發映射

性能影響

  • 相比 HashMap,因維護鏈表會略微增加內存和時間開銷
LinkedHashMap在HashMap的基礎上增加了鏈表結構,數據結構就變成了 雙向鏈表+ HashMap的數據結構

簡單來說 LinkedHashMap = HashMap的哈希桶 + 追加的雙向鏈表,所以有額外開銷

  • 迭代效率更高(直接遍歷鏈表,無需像 HashMap 那樣遍歷桶數組)
LinkedHashMap遍歷:按照元素的插入順序(或訪問順序)進行訪問,訪問的是鏈表
HashMap遍歷:先哈希導航定位桶,然后桶內鏈表或者樹在遍歷

構造方法

關鍵參數說明:
initialCapacity:初始哈希表容量 默認值16
loadFactor: 負載因子 默認值0.75

擴容閾值 = 容量 × 負載因子
當前元素數量 size
如果當前元素數量大于擴容閾值 了,那么哈希表就需要擴容,二倍擴容 從16變成32

accessOrder 順序模式控制參數:false = 插入順序(默認),true = 訪問順序(LRU)

1.LinkedHashMap()
默認插入順序,初始容量 16,負載因子 0.75

LinkedHashMap<String, Integer> map1 = new LinkedHashMap<>();

2.LinkedHashMap(int initialCapacity)
插入順序,指定初始容量 ,負載因子 0.75

// 初始容量 32
LinkedHashMap<String, Integer> map2 = new LinkedHashMap<>(32);

3.LinkedHashMap(int initialCapacity, float loadFactor)
插入順序,指定初始容量和負載因子

// 初始容量 64,負載因子 0.8
LinkedHashMap<String, Integer> map3 = new LinkedHashMap<>(64, 0.8f);

4.LinkedHashMap(Map<? extends K, ? extends V> m)
從現有 Map 創建(保持源 Map 的迭代順序)

    public static void main(String[] args) {Map<String, Integer> map = new HashMap<>();map.put("Apple",1);map.put("Bob",2);map.put("Charlie",3);map.put("Dave",4);map.put("Error",5);map.forEach((K,V)->System.out.println(K));System.out.println("==================================");// 復制 map 的所有元素LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>(map);linkedHashMap.forEach((K,V)->System.out.println(K));}

需要注意的是:LinkedHashMap復制map的所有元素是 按照map遍歷的順序,并不是map最初元素的插入順序,這個構造函數服務對象是 map

5.LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
指定順序模式:關鍵參數:accessOrder=true 時啟用訪問順序

// 初始容量 16,負載因子 0.75,訪問順序模式
LinkedHashMap<String, Integer> map5 = new LinkedHashMap<>(16, 0.75f, true  // true=訪問順序,false=插入順序
);

核心操作邏輯(雙向鏈表維護介紹)

節點結構(繼承自 HashMap.Node)比起HashMap 多了前后節點

		// HashMap.Node屬性:final int hash;final K key;V value;Node<K,V> next;
//LinkedHashMap 節點繼承HashMap.Node節點多了 前后兩個指針
static class Entry<K,V> extends HashMap.Node<K,V> {// 雙向鏈表指針Entry<K,V> before, after;Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}
}

類屬性

// 雙向鏈表頭節點(最舊元素)
transient LinkedHashMap.Entry<K,V> head;// 雙向鏈表尾節點(最新元素)
transient LinkedHashMap.Entry<K,V> tail;// 順序模式標志
final boolean accessOrder; // true=訪問順序, false=插入順序

插入操作(put)

調用HashMap.putVal()的新增節點

//先檢查哈希表
//然后哈希定位桶是否為空,為空說明是該桶第一個節點 直接放入
if ((p = tab[i = (n - 1) & hash]) == null)//創建節點放入桶中 tab[i] = newNode(hash, key, value, null);else {//桶不為空 就需要查找合適節點位置處理//..哈希導航定位過程.....//// 找到了合適位置 創建節點準備放入桶內p.next = newNode(hash, key, value, null);//..存放節點入桶.....//

這些步驟和HashMap新增節點數據一致,區別就在于這里的newNode

linkNodeLast

創建新節點(LinkedHashMap里面重寫 HashMap.newNode)

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {// 1. 創建LinkedHashMap特有節點(含雙向指針)LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<>(hash, key, value, e);// 2. 將新節點鏈接到鏈表尾部linkNodeLast(p);return p;
}private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {// 獲取當前尾節點LinkedHashMap.Entry<K,V> last = tail;// 更新尾指針:新節點成為尾節點tail = p;if (last == null) {// 鏈表為空,設置成頭節點head = p;} else {// 鏈表非空,鏈接新節點:將新節點插入鏈表尾部p.before = last;last.after = p;}
}

簡單說:LinkedHashMap = HashMap的哈希桶 + 追加的雙向鏈表,并且通過在節點創建時鏈接到獨立雙向鏈表(linkNodeLast())實現順序跟蹤,與哈希桶結構維護并行存在并且是優于該操作的

afterNodeAccess

如果新增節點時當前鍵已經存在 就需要更新原值,對于LinkedHashMap的雙向鏈表同樣需要更新

            if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}//hashMap的afterNodeAccess方法沒有任何執行邏輯,在LinkedHashMap進行重寫void afterNodeAccess(Node<K,V> p) { }

核心作用:將節點移動到雙向鏈表尾部(尾部 = 最近訪問)

必要條件:
1.accessOrder = true 代表訪問順序模式,隨著訪問更改插入先后順序,更新值也相當于訪問
2.當前節點不是尾節點,是尾巴不用移動了

// 在HashMap的getNode()/putVal()等方法中觸發
void afterNodeAccess(Node<K,V> e) { // e = 被訪問的節點LinkedHashMap.Entry<K,V> last = tail;// 條件1: 需要按訪問排序// 條件2: 當前節點不是尾節點(已在尾部則無需移動)if (accessOrder && (last = tail) != e) {// 類型轉換 + 獲取鄰居引用LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e;LinkedHashMap.Entry<K,V> b = p.before; // 前驅節點LinkedHashMap.Entry<K,V> a = p.after;  // 后繼節點// 步驟1: 斷開當前節點后繼連接p.after = null;// 步驟2: 處理前驅節點if (b == null) head = a;       // 情況1: 當前是頭節點 → 新頭節點變為后繼else b.after = a;    // 情況2: 普通節點 → 前驅指向后繼// 步驟3: 處理后繼節點if (a != null) a.before = b;   // 后繼存在 → 后繼指向前驅else last = b;       // 后繼為空 → 尾指針前移(此時p是尾節點,但已排除)// 步驟4: 處理鏈表為空情況if (last == null)head = p;       // 鏈表僅剩p一個節點else {// 步驟5: 將p鏈接到鏈表尾部p.before = last;  // p前驅指向原尾節點last.after = p;   // 原尾節點后繼指向p}// 步驟6: 更新尾指針tail = p;           // p成為新尾節點// 步驟7: 結構修改計數++modCount;         // 保證迭代器快速失敗}
}

除了新增節點已存在鍵情況下更新并且是訪問模式觸發該調整元素順序操作之外

get(key) 獲取存在的鍵
getOrDefault(key, defaultValue) 獲取存在的鍵
put(key, value) 更新已存在的鍵值對
putIfAbsent(key, value) 更新已存在的鍵值對
compute / merge 等更新操作

afterNodeInsertion

在hashMap.putVal方法尾巴,新增節點成功返回之前 調用該方法,所以叫插入回調

        ++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}//同樣hashMap中還是沒有具體執行內容,由LinkedHashMap理覆蓋重寫void afterNodeInsertion(boolean evict) { }

核心作用:在插入新節點后,根據 removeEldestEntry() 決定是否移除鏈表頭節點(最久未使用)

void afterNodeInsertion(boolean evict) {LinkedHashMap.Entry<K,V> first;// 條件1: evict=true(表示處于活動狀態)// 條件2: 鏈表非空(first=head不為null)// 條件3: removeEldestEntry(first) 返回trueif (evict && (first = head) != null && removeEldestEntry(first)) {// 獲取待刪除節點的keyK key = first.key;// 調用HashMap的removeNode方法removeNode(hash(key),      // 計算key的哈希值key,            // 待刪除的keynull,           // value=null(匹配任意值)false,          // matchValue=false(不匹配value)true            // movable=true(允許移動節點));}
}// 需開發者重寫的方法(默認返回false)
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false; // 默認不刪除舊節點
}

典型應用場景(LRU緩存):

// 固定大小的LRU緩存實現
public class LRUCache<K,V> extends LinkedHashMap<K,V> {private final int MAX_SIZE;public LRUCache(int maxSize) {super(16, 0.75f, true); // 開啟訪問順序排序this.MAX_SIZE = maxSize;}// 重寫刪除策略:當大小超過閾值時刪除頭節點@Overrideprotected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return size() > MAX_SIZE;}
}

移除操作(remove)

還是調用HashMap.removeNode移除節點,先執行哈希桶內數據的移除 調整

				// 在執行HashMap.removeNode即將結束返回之前 回調方法afterNodeRemovalafterNodeRemoval(node);return node;//同樣hashMap中還是沒有具體執行內容,由LinkedHashMap理覆蓋重寫void afterNodeRemoval(Node<K,V> p) { }

作用:正常刪除,從雙向鏈表中解除節點鏈接,更新相鄰節點指針

void afterNodeRemoval(Node<K,V> e) {// 類型轉換LinkedHashMap.Entry<K,V> p = (Entry<K,V>)e;LinkedHashMap.Entry<K,V> b = p.before;LinkedHashMap.Entry<K,V> a = p.after;// 完全斷開節點鏈接p.before = p.after = null;if (b == null) {// 當前節點是頭節點head = a;} else {b.after = a;}if (a == null) {// 當前節點是尾節點tail = b;} else {a.before = b;}
}

迭代遍歷操作(Iterator)

LinkedHashMap的迭代方法

		Map<String,Integer> map = new LinkedHashMap<>();System.out.println("Keys: " + map.keySet());System.out.println("Values: " + map.values());System.out.println("Entries: " + map.entrySet());

雖然HashMap中有相同的方法,但是LinkedHashMap迭代方法上沒有使用HashMap而是自身迭代器

// 按照LinkedHashMap自身維護的雙向鏈表 從頭到尾遍歷
abstract class LinkedHashIterator {LinkedHashMap.Entry<K,V> next;LinkedHashMap.Entry<K,V> current;LinkedHashIterator() {// 直接從鏈表頭開始迭代next = head;current = null;}public final boolean hasNext() {return next != null;}final LinkedHashMap.Entry<K,V> nextNode() {LinkedHashMap.Entry<K,V> e = next;if (e == null)throw new NoSuchElementException();current = e;next = e.after; // 沿鏈表順序遍歷return e;}}

常見應用場景

1.需要保留插入順序的映射:基于插入順序
(如:記錄用戶操作序列)

    public static void main(String[] args) {Map<String, String> operationSequence = new LinkedHashMap<>();Date date = new Date();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");operationSequence.put("登錄", "當前時間為:" + dateFormat.format(date));operationSequence.put("查看首頁", "當前時間為:" + dateFormat.format(date));operationSequence.put("搜索商品", "當前時間為:" + dateFormat.format(date));operationSequence.put("添加到購物車", "當前時間為:" + dateFormat.format(date));for (Map.Entry<String, String> entry : operationSequence.entrySet()) {System.out.println("操作: " + entry.getKey() + ", 時間: " + entry.getValue());}}

2.實現LRU緩存淘汰策略:基于訪問順序
(如:緩存系統資源池管理
實現 LRU 緩存: 是一種緩存淘汰策略,優先淘汰最近最少使用的數據,以便為新數據騰出空間
通過重寫 removeEldestEntry() 方法,當元素數量超過上限時自動刪除最舊條目

final int MAX_SIZE = 3;
LinkedHashMap<String, String> cache = new LinkedHashMap<>(16, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<String, String> eldest) {return size() > MAX_SIZE; // 容量超限時刪除最舊元素}
};cache.put("Key1", "Value1");
cache.put("Key2", "Value2");
cache.put("Key3", "Value3");
cache.put("Key4", "Value4"); // 加入 Key4 后,Key1 會被自動移除

3.需要可預測迭代順序的場景:基于插入順序和訪問順序的有序性
(替代 HashMap 的無序性)

    public static void main(String[] args) {// 創建一個新的LinkedHashMap實例Map<String, String> map = new LinkedHashMap<>();// 向map中添加元素map.put("Z", "Zebra");map.put("A", "Ant");map.put("M", "Monkey");// 遍歷map,輸出元素for (Map.Entry<String, String> entry : map.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}}

注意事項

1.非線程安全:多線程環境下需用 Collections.synchronizedMap 包裝。
2.訪問順序的影響:put、get、putIfAbsent 等操作都會更新鏈表順序。
3.性能權衡:在保證順序的同時,比 HashMap 稍慢(但迭代更快)。

總結

LinkedHashMap是一個非常有用的數據結構,它結合了哈希表和鏈表的優點,既提供了高效的查找操作,又保持了元素的順序。特別適合需要保持插入順序或實現LRU緩存的場景,而HashMap適合當不需要保持元素順序且追求快速查找性能場景使用

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

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

相關文章

MySQL 離線安裝MariaDB

描述 離線環境下安裝MySQL數據庫&#xff0c;也就是MariaDB 操作 1、找到自帶的mysql rpm -qa | grep -i ^mysql-rpm -qa | grep -i ^maria-2、卸載對應的包 rpm --nodeps -ev mysql-libs-5.1.73-8.el6_8.x86_64安裝 MariaDb 離線安裝包官網下載&#xff1a;地址 這個文…

JSON簡介及其應用

JSON簡介及其應用 A Brief Introduction and Applications of JSON By JacksonML 1. JSON的概念 JSON&#xff08;JavaScript Object Notation&#xff09; 是一種輕量級的數據交換格式&#xff0c;采用鍵值對&#xff08;key-value&#xff09;的方式組織數據&#xff0c;…

RNN(循環神經網絡)與LSTM(長短期記憶網絡)輸出的詳細對比分析

今天在與同事探討RNN時&#xff0c;引出了一個主題&#xff0c;RNN和LSTM的輸出有什么區別。 以下是關于傳統RNN&#xff08;循環神經網絡&#xff09;與LSTM&#xff08;長短期記憶網絡&#xff09;隱藏層內容、輸出結果及模型區別的詳細對比分析&#xff0c;結合結構原理、數…

【閑談】技術債:軟件開發的隱形殺手

編程中的“技術債”&#xff1a;隱形殺手與化解之道 在軟件開發的世界里&#xff0c;我們常談性能、安全、架構設計、用戶體驗等話題&#xff0c;但有一個常被忽視的概念卻如影隨形、悄然吞噬著項目的健康——技術債&#xff08;Technical Debt&#xff09;。 本文將帶你深入…

Elasticsearch | 索引和模板字段管理:增加新字段的詳細操作

關注CodingTechWork 背景介紹 Elasticsearch 是一款基于 Lucene 的搜索和數據分析引擎&#xff0c;廣泛應用于日志分析、全文檢索等領域。在使用 Elasticsearch 時&#xff0c;字段是存儲在索引中的數據單位&#xff0c;字段的定義決定了數據的存儲方式及其檢索效率。因此&…

HTML表格中<tfoot>標簽用法詳解

在HTML中&#xff0c;<tfoot>標簽用于定義表格的頁腳&#xff08;表腳&#xff09;&#xff0c;通常包含匯總信息&#xff08;如總計、平均值等&#xff09;。其核心特點和使用方法如下&#xff1a; 基本特性 位置靈活 <tfoot>必須位于<table>內&#xff0c…

深度學習正負樣本比例的影響及其調節方法

在深度學習中&#xff0c;數據是模型性能的決定性因素之一。特別是在二分類問題中&#xff0c;正負樣本的比例對模型訓練的影響尤為顯著。本文將探討正負樣本比例對深度學習的影響&#xff0c;并給出相應的調節方法和代碼示例。 什么是正負樣本比例&#xff1f; 在二分類問題…

【公司經營】安全公司產品經營

一、產品經營 1.1 產品矩陣設計方法&#xff1a;風險場景驅動? ?分層產品架構? ?基礎層?&#xff1a;防火墻/WAF/EDR&#xff08;標準化硬件軟件&#xff09;?分析層?&#xff1a;SOC平臺/XDR&#xff08;年訂閱制&#xff0c;SaaS化交付&#xff09;?響應層?&#…

鴻蒙 Scroll 組件深度解析:絲滑滾動交互全場景實現

一、引言&#xff1a;Scroll—— 內容溢出場景的交互中樞 在鴻蒙應用開發中&#xff0c;當界面內容超出屏幕可視范圍時&#xff0c;Scroll 容器組件成為實現流暢滾動交互的核心方案。作為從 API 7 開始支持的基礎組件&#xff0c;它通過極簡的屬性配置與強大的滾動控制能力&am…

第十節:Vben Admin 最新 v5.0 (vben5) 快速入門 - 菜單管理(下)

Vben5 系列文章目錄 ?? 基礎篇 ? 第一節:Vben Admin 最新 v5.0 (vben5) 快速入門 ? 第二節:Vben Admin 最新 v5.0 (vben5) 快速入門 - Python Flask 后端開發詳解(附源碼) ? 第三節:Vben Admin 最新 v5.0 (vben5) 快速入門 - 對接后端登錄接口(上) ? 第四節:Vben Ad…

c#激光設備行業ERP軟件進銷存軟件庫存管理軟件財務管理軟件

# 激光設備行業ERP軟件進銷存軟件庫存管理軟件財務管理軟件 # 開發背景 本軟件是給廣東河源某客戶開發的激光設備行業ERP軟件進銷存軟件庫存管理軟件財務管理軟件 # 功能描述 軟件由基礎資料、庫存管理、 屬性管理、 用戶管理、 銷售管理、 財務管理。主要功能模塊是庫存管理…

python學習打卡day57

DAY 57 經典時序模型1 知識點回顧 序列數據的處理&#xff1a; 處理非平穩性&#xff1a;n階差分處理季節性&#xff1a;季節性差分自回歸性無需處理 模型的選擇 AR(p) 自回歸模型&#xff1a;當前值受到過去p個值的影響MA(q) 移動平均模型&#xff1a;當前值收到短期沖擊的影響…

python小記(十七):Python 使用“繼承”來管理yaml文件

Python 使用“繼承”來管理yaml文件 引言 引言 在 Python 中有時候我們會把參數都儲存在yaml文件中然后進行調用。當我們在進行一個很大的項目的時候&#xff0c;我們可能先需要一個base.yaml文件&#xff0c;然后再使用一個task1.yaml文件進行參數導入&#xff0c;并且task1.…

Windows搭建opencv cuda開發環境并驗證是否成功

編譯opencv cuda源碼 電腦安裝cuda 12.0或者11.8&#xff0c;根據你的電腦配置自行選擇 下載opencv 源碼 git clone https://github.com/opencv/opencv.git git clone https://github.com/opencv/opencv_contrib.git 在opencv目錄里新建 build 文件夾 cd build后 cmake…

【go】初學者入門環境配置,GOPATH,GOROOT,GOCACHE,以及GoLand使用配置注意

一、環境變量配置步驟 1. 打開環境變量設置 Win R 后輸入 sysdm.cpl → 點擊 確定在彈出窗口中點擊 高級 → 環境變量 2. 配置 GOROOT&#xff08;Go語言安裝根目錄&#xff09; 作用&#xff1a;告訴系統Go語言的安裝位置&#xff08;編譯器、標準庫等核心文件所在路徑&a…

gantt-task-react的改造使用

gantt-task-react的鏡像地址 例子 改造1&#xff1a;切斷父子關聯關系&#xff0c;父為project組件&#xff0c;子為task組件&#xff0c; 原來的功能是task組件拖動會影響到父組件&#xff0c;現在切斷兩者關聯關系&#xff0c;數據都用task組件&#xff0c; 給task組件重…

kotlin 協程(Coroutine)

Coroutine&#xff08;協程&#xff09;的轉換原理&#xff1a; 在 kotlin 中&#xff0c;Coroution 是一種輕量級的線程管理方式&#xff0c;其轉換原理涉及 狀態機生成、掛起函數轉換和調度器機制。 一、協程的本質&#xff1a;狀態機 kotlin 協程通過 編譯器生成狀態機 實…

線性變換之維數公式(秩-零化度定理)

秩數-零化度定理(rank-nullity theorem) 目錄 1. (映射)零空間(線性映射或變換的核)(null-space或nullspace) 2. 跨度(或開度)(span) 3. (線性映射的)零化度(nullity) 4. 線性變換的維數公式(秩數-零化度定理)(rank-nullity theorem) 5. 函數的上域(codomain) 1…

Spring Cloud Gateway 實戰:網關配置與 Sentinel 限流詳解

Spring Cloud Gateway 實戰&#xff1a;網關配置與 Sentinel 限流詳解 在微服務架構中&#xff0c;網關扮演著統一入口、負載均衡、安全認證、限流等多種角色。Spring Cloud Gateway 是 Spring Cloud 官方推出的新一代網關組件&#xff0c;相比于第一代 Netflix Zuul&#xff…

JAVA-常用API(二)

目錄 1.Arrays 1.1認識Arrays 1.2Arrays的排序 2.JDK8的新特性&#xff1a;Lambda表達式 2.1認識Lambda表達式 2.2用Lambda表達式簡化代碼、省略規則 3.JDK8的新特性&#xff1a;方法引用&#xff08;進一步簡化Lambda表達式&#xff09; 3.1 靜態方法引用 3.2 實例方法引…