LRU(Least Recently Used,最近最少使用)是一种常见的缓存淘汰策略,核心思想是当缓存满时,优先淘汰最近最少使用的元素。
get先检查哈希表是否存在key,存在去链表中获取,更新key到链表头部,同时更新hash表key的下标 put时候,同样操作,只是对于不存在的key放在链表尾部,同时检查容量是否满了,进行删除尾部key
在 Go 中实现 LRU 缓存,通常可以结合哈希表( map )和双向链表来实现,哈希表用于快速查找,双向链表用于维护元素的访问顺序。以下是一个简单实现:
package main
import "container/list"
// LRUCache 表示LRU缓存
type LRUCache struct {
capacity int // 缓存容量
cache map[int]*list.Element // 哈希表,键为缓存键,值为链表节点
list *list.List // 双向链表,用于维护访问顺序,节点值为键值对
}
// entry 表示链表节点中存储的键值对
type entry struct {
key int
value int
}
// NewLRUCache 初始化一个LRU缓存
func NewLRUCache(capacity int) *LRUCache {
return &LRUCache{
capacity: capacity,
cache: make(map[int]*list.Element),
list: list.New(),
}
}
// Get 获取缓存中的值
func (c *LRUCache) Get(key int) int {
// 如果键存在
if elem, ok := c.cache[key]; ok {
// 将节点移到链表头部(表示最近使用)
c.list.MoveToFront(elem)
return elem.Value.(*entry).value
}
return -1 // 键不存在时返回-1
}
// Put 向缓存中添加或更新键值对
func (c *LRUCache) Put(key int, value int) {
// 如果键已存在
if elem, ok := c.cache[key]; ok {
// 更新值
elem.Value.(*entry).value = value
// 将节点移到链表头部
c.list.MoveToFront(elem)
return
}
// 键不存在,检查缓存是否已满
if c.list.Len() == c.capacity {
// 缓存满,删除链表尾部节点(最近最少使用)
tail := c.list.Back()
if tail != nil {
delete(c.cache, tail.Value.(*entry).key)
c.list.Remove(tail)
}
}
// 添加新节点到链表头部
elem := c.list.PushFront(&entry{key: key, value: value})
c.cache[key] = elem
}
代码说明:
- 核心结构 LRUCache 包含缓存容量、哈希表和双向链表。
- 双向链表 list 用于记录元素的访问顺序,最近使用的元素放在头部,最少使用的在尾部。
- 哈希表 cache 用于快速根据键找到对应的链表节点,时间复杂度为 O(1)。
- Get 方法:若键存在,将对应节点移到链表头部(标记为最近使用)并返回值;否则返回 -1。
- Put 方法:若键存在则更新值并移到头部;若不存在,先检查容量,满则删除尾部节点,再添加新节点到头部。
这个实现的 Get 和 Put 操作时间复杂度均为 O(1),符合 LRU 缓存的高效性要求。