十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LRU 缓存实现:先把链表原语和边界条件写清楚

LRU 缓存实现:先把链表原语和边界条件写清楚 LRU 缓存实现先把链表原语和边界条件写清楚LRU 的常见写法是哈希表加双向链表哈希表按 key 找节点链表记录最近使用顺序。只要两种操作都保持常数次指针变更Get和Put的平均时间复杂度就是 O(1)。难点不在概念而在淘汰和移动节点时保持两个结构同步。让指针变更集中用虚拟头尾节点可减少空链表和首尾节点的分支。链表只负责插入、删除和取尾节点缓存对象负责 map、容量和调用顺序。这样单元测试可以分别覆盖链表和缓存。type node struct { key, value int prev, next *node } func (c *Cache) detach(n *node) { n.prev.next n.next n.next.prev n.prev } func (c *Cache) attachFront(n *node) { n.next c.head.next n.prev c.head c.head.next.prev n c.head.next n } func (c *Cache) Get(key int) (int, bool) { n, ok : c.items[key] if !ok { return 0, false } c.detach(n) c.attachFront(n) return n.value, true }完整的Put需要覆盖已有 key、容量为零、满容量淘汰和新节点插入。淘汰尾节点时先从链表删除再从 map 删除节点保留 key 正是为了完成这一步。工程中的几个限制这段代码本身不是并发安全的。并发缓存可在外层加互斥锁或按 key 分片RWMutex未必有收益因为Get也会移动节点是写操作。容量、TTL、缓存穿透和缓存预热属于上层策略不能由一个 LRU 结构自动解决。测试至少包含连续覆盖同一 key、容量为一、淘汰后重新写入、随机操作与参考 map 的结果对照。不要把纳秒级基准数字当作通用结论分配、锁和 value 大小都会改变结果。把链表原语拆开不是为了让代码“更高级”而是让每一次指针修改都有唯一的位置可检查。
返回列表