数据结构 - 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 的高效查找,又能维护元素的顺序,是一个非常精妙的设计。

相关推荐
闻闻不会编程5 分钟前
704. 二分查找 (力扣)
数据结构·算法·leetcode
bxlj_jcj23 分钟前
解锁Java多级缓存:性能飞升的秘密武器
java·缓存·面试·架构
Fastcv29 分钟前
手把手教你上传安卓库到Central Portal
android·maven·jcenter
whysqwhw32 分钟前
安卓应用线程与架构问题
android
小鱼干coc32 分钟前
Android 轻松实现 增强版灵活的 滑动式表格视图
android
工呈士36 分钟前
React 性能监控与错误上报
前端·react.js·面试
前端小巷子41 分钟前
JS中的 eval
前端·javascript·面试
Dignity_呱1 小时前
玩转Vue插槽:从基础到高级应用场景(内含为何Vue 2 不支持多根节点)
前端·vue.js·面试
Le_ee1 小时前
dvwa6——Insecure CAPTCHA
android·安全·网络安全·靶场·dvwa
异常君1 小时前
Java 逃逸分析:让你的代码性能飙升的秘密
java·面试·代码规范