当前位置: 首页 > news >正文

LRU 结构 LinkedHashMap:HashMap+双向链表的完美结合

LinkedHashMap = HashMap + 双向链表,在HashMap的基础上增加了插入/访问顺序的维护能力。

核心结构组件

1. Entry节点设计

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.Node,保持哈希表功能
  • 添加before/after指针维护插入顺序
  • 一个节点同时参与两种数据结构:哈希表的链表/红黑树 + 全局双向链表

2. 链表头尾指针

transient LinkedHashMap.Entry<K,V> head;  // 最老的节点
transient LinkedHashMap.Entry<K,V> tail;  // 最新的节点

三种排序模式机制

1. 插入顺序模式(默认)

final boolean accessOrder = false;  // 插入顺序

2. 访问顺序模式

final boolean accessOrder = true;   // LRU语义

3. 动态定位模式(Java 21新特性)

static final int PUT_NORM = 0;   // 正常模式
static final int PUT_FIRST = 1;  // 插入到头部
static final int PUT_LAST = 2;   // 插入到尾部
transient int putMode = PUT_NORM;

深入分析有意思的设计点

1. 优雅的Hook机制

LinkedHashMap通过重写HashMap的钩子方法实现功能扩展:

// 节点插入后的回调
void afterNodeInsertion(boolean evict) {LinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {K key = first.key;removeNode(hash(key), key, null, false, true);  // 自动淘汰}
}// 节点访问后的回调
void afterNodeAccess(Node<K,V> e) {// 在访问顺序模式下,将访问的节点移动到链表尾部if (accessOrder && (last = tail) != e) {// 复杂的链表重新链接逻辑}
}

设计精髓:通过Template Method模式,在不修改HashMap核心逻辑的前提下,优雅地扩展了功能。

2. 高效的链表操作

节点链接操作

private void linkNodeAtEnd(LinkedHashMap.Entry<K,V> p) {if (putMode == PUT_FIRST) {// 插入到头部LinkedHashMap.Entry<K,V> first = head;head = p;if (first == null) tail = p;else {p.after = first;first.before = p;}} else {// 插入到尾部LinkedHashMap.Entry<K,V> last = tail;tail = p;if (last == null) head = p;else {p.before = last;last.after = p;}}
}

亮点

  • 统一的链接逻辑处理头部/尾部插入
  • 边界条件处理完善(空链表情况)
  • O(1)时间复杂度

3. LRU缓存的天然支持

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false;  // 默认不删除,子类可重写实现LRU
}

使用示例

LinkedHashMap<String, String> lruCache = new LinkedHashMap<String, String>(16, 0.75f, true) {protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {return size() > MAX_ENTRIES;  // 超过阈值就删除最老的}
};

4. SequencedMap接口的实现(Java 21)

LinkedHashMap实现了新的SequencedMap接口,提供了:

public V putFirst(K k, V v) {try {putMode = PUT_FIRST;return this.put(k, v);} finally {putMode = PUT_NORM;  // 确保模式重置}
}public SequencedMap<K, V> reversed() {return new ReversedLinkedHashMapView<>(this);
}

设计巧思

  • 通过临时设置putMode实现定位插入
  • 使用try-finally确保状态恢复
  • 反向视图通过包装器模式实现,避免数据复制

5. 迭代器的优化设计

abstract class LinkedHashIterator {LinkedHashMap.Entry<K,V> next;boolean reversed;LinkedHashIterator(boolean reversed) {this.reversed = reversed;next = reversed ? tail : head;  // 根据方向选择起点}final LinkedHashMap.Entry<K,V> nextNode() {// ...next = reversed ? e.before : e.after;  // 根据方向移动return e;}
}

性能优势

  • 迭代时间复杂度O(n),与容量无关
  • 支持正向/反向迭代
  • 相比HashMap的O(capacity)有显著优势

6. 内存布局的巧妙设计

每个Entry节点同时维护:

  • 哈希表关系next指针(链表)或红黑树关系
  • 插入顺序关系before/after指针(双向链表)

这种设计让一个节点同时参与两种数据结构,内存效率高且逻辑清晰。

相关文章:

  • 用python玩转大语言模型——从 RNN 到文本生成大语言模型的奇幻之旅
  • MMDG++:构筑多模态人脸防伪新防线,攻克伪造攻击与场景漂移挑战
  • 日期的数据格式转换
  • 爬取新浪新闻网的全部策略
  • Go 语言 JWT 深度集成指南
  • 升级 Ubuntu Linux 内核的几种不同方法
  • Squid 代理服务器实战:解决动态 IP 访问第三方接口的生产级方案
  • 软件定义对象存储购买指南
  • 数据库游标:逐行处理数据的“手术刀”——从原理到实战的深度解析
  • 链 表 类 型 全 面 总 结:单 向、双 向、循 环 链 表 的 特 性 与 选 型 指 南
  • PSCAD closed loop buck converter
  • 同步发电机原理
  • Java并发工具包
  • 图标异常问题
  • pysnmp模块中 GET、SET、WALK操作详细分步解析
  • 【论文解读】Search-o1:用 Agentic 搜索增强推理模型
  • RK3288项目(六)--linux内核之双摄(ov9281)的使用
  • 几个常见远程工作平台
  • 使用MyBatis-Plus实现数据权限功能
  • 【排错】ubuntu挂载硬盘mount报错 unknown filesystem type ‘LVM2_member‘.
  • 搭建论坛网站/百度快速排名培训
  • 做网站公司价格/变现流量推广app
  • 移动端网站设计尺寸/百度快照搜索引擎
  • 天河区网站公司/谷歌网站
  • wordpress多个字体大小/seo推广一个月见效
  • 越秀区营销型网站建设/关键词优化是什么意思