数据结构 - LinkedHashMap(二)

1. 基本结构

java 复制代码
public class LinkedHashMap<K,V> extends HashMap<K,V> {
    // 双向链表的头尾节点
    transient LinkedHashMap.Entry<K,V> head;    // 头节点
    transient LinkedHashMap.Entry<K,V> tail;    // 尾节点
    
    // 节点定义
    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);
        }
    }
}

2. 数据结构图解

graph LR A[HashMap数组] --> B[链表/红黑树] C[双向链表] --> D[按插入顺序] subgraph "HashMap结构" A --> E[桶0] A --> F[桶1] A --> G[桶2] end subgraph "双向链表" H[头节点] --> I[节点1] I --> J[节点2] J --> K[尾节点] K --> J J --> I I --> H end

3. 插入过程

java 复制代码
// 1. 继承自HashMap的put方法
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

// 2. 在节点插入后,会调用afterNodeInsertion方法
void afterNodeInsertion(boolean evict) {
    LinkedHashMap.Entry<K,V> first;
    // 维护插入顺序
    if (evict && (first = head) != null) {
        removeEldestEntry(first);
    }
}

4. 实际例子

java 复制代码
LinkedHashMap<String, String> map = new LinkedHashMap<>();

// 1. 插入元素
map.put("A", "1");  // 第一个
map.put("B", "2");  // 第二个
map.put("C", "3");  // 第三个

// 2. 遍历 - 会按插入顺序输出
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 输出:
// A:1
// B:2
// C:3

5. 两种结构的配合

graph TD A[LinkedHashMap] --> B[HashMap结构] A --> C[双向链表] B --> D[快速查找] C --> E[维护顺序] subgraph "职责分工" D --> F[O1时间复杂度访问] E --> G[记录插入/访问顺序] end

6. 特殊功能

6.1 按访问顺序排序

java 复制代码
// 创建按访问顺序排序的LinkedHashMap
LinkedHashMap<String, String> accessMap = 
    new LinkedHashMap<>(16, 0.75f, true);

accessMap.put("A", "1");
accessMap.put("B", "2");
accessMap.put("C", "3");

accessMap.get("A");  // 访问A
// 现在A会移到最后

// 遍历顺序:B -> C -> A

7. LRU缓存实现

java 复制代码
class LRUCache<K,V> extends LinkedHashMap<K,V> {
    private final int capacity;
    
    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);  // 按访问顺序
        this.capacity = capacity;
    }
    
    @Override
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return size() > capacity;  // 超过容量则移除最老的
    }
}

8. 实际应用场景

java 复制代码
// 1. 需要记录插入顺序
LinkedHashMap<String, User> userMap = new LinkedHashMap<>();
userMap.put("user1", new User("张三"));
userMap.put("user2", new User("李四"));
// 遍历时保证按插入顺序

// 2. LRU缓存
LRUCache<String, Data> cache = new LRUCache<>(100);
cache.put("key1", data1);
cache.get("key1");  // 访问会更新顺序

9. 性能特点

markdown 复制代码
1. 查找性能:O(1),继承自HashMap
2. 插入性能:O(1),但比HashMap略慢(需要维护链表)
3. 内存消耗:比HashMap大(额外的链表指针)

10. 总结配合方式

  1. HashMap 负责:

    • 存储节点
    • 快速查找
    • 哈希计算
  2. 双向链表负责:

    • 维护顺序
    • 快速插入/删除
    • 迭代顺序

就像是:

  • HashMap是一个仓库(负责存储)
  • 双向链表是一个记录本(负责记录顺序)
  • 两者配合提供了"有序的Map"功能

这种组合让 LinkedHashMap 既拥有了 HashMap 的高效查找,又能维护元素的顺序,是一个非常精妙的设计。

相关推荐
笑鸿的学习笔记3 分钟前
leetcode-442.数组中重复的数据
数据结构·算法·leetcode
何政@11 分钟前
Mysql面试篇笔记:
java·数据库·spring boot·笔记·mysql·面试
醉城夜风~19 分钟前
[数据结构]单值二叉树
数据结构·算法
刃神太酷啦27 分钟前
C++(蓝桥杯常考点)
数据结构·c++·蓝桥杯c++组
星空露珠34 分钟前
迷你世界脚本世界接口:World
数据结构·游戏·lua
星空露珠42 分钟前
迷你世界脚本方块接口:Block
数据结构·游戏·lua
柃歌1 小时前
【UCB CS 61B SP24】Lecture 19 & 20: Hashing & Hashing II 学习笔记
java·数据结构·笔记·学习·算法
HBryce241 小时前
《数据结构》
java·数据结构
PBitW2 小时前
阅读《Vue.js设计与实现》 -- 02
前端·vue.js·面试
咩咩觉主2 小时前
C# &Unity 唐老狮 No.3 模拟面试题
unity·面试·c#·唐老师