
LRU缓存算法
发布日期:2021-05-08 04:53:56
浏览次数:24
分类:原创文章
本文共 3565 字,大约阅读时间需要 11 分钟。
注:本文使用golang语言表述。
LRU(least recently used)是一个缓存剔除策略算法,在缓存容量不足的时候,将最不常用的一个或多个缓存相剔除,腾出空间以便后续缓存使用。
实现一个LRU cache
LRU cache
可以使用两个数据结构来表示。
- 一个
hashtable
,用来存储需要存储的缓存项,这样,get/set
操作可以在O(1)
时间复杂度下完成。 - 一个
doubly list
用来记录缓存项的最近使用情况,例如,如果最近使用过的项,则该项在doubly list
的队首,最不常用的项在队尾。正是经常使用队首队尾,所以才使用doubly list
数据结构。
首先对需要缓存的对象进行抽象,用一个interface来表示。
type Cacheable interface { Key() string Size() int}
对缓存项定义
type LRUCacheItem struct { cacheable Cacheable listElement *list.Element}
缓存项LRUCacheItem
里记录了需要缓存对对象以及其对应的链表元素的指针,这个指针用来快速操作缓存链表,后面会详细表述。
对整体缓存定义
type LRUCache struct { capcity int items map[string]*LRUCacheItem list *list.List}
缓存LRUCache
包含三个字段:capcity
表示当前缓存剩余容量。items
是一个hashtable,用来记录LRUCacheItem
,list
用来给LRUCacheItem
排队。
数据结构都定义好了,下面生成一个cache
。
func New(capcity int) *LRUCache { return &LRUCache{ capcity: capcity, items: make(map[string]*LRUCacheItem), list: list.New(), }}
定义get
操作:
func (c *LRUCache) get(key string) Cacheable { item, exists := c.items[key] if !exists { return nil } c.promote(item) // 因为刚刚用到,所以将该item提到队首 return item.cacheable}// 将item提到队首func (c *LRUCache) promote(item *LRUCacheItem) { c.list.MoveToFront(item.listElement)}
定义set
操作:
func (c *LRUCache) set(cacheable Cacheable) bool { if c.capcity < cacheable.Size() { c.prune() // 空间不足,剔除不常用队缓存项 } if c.capcity < cacheable.Size() { return false } if item, exists := c.items[cacheable.Key()]; exists { c.promote(item) return true } key := cacheable.Key() item := &LRUCacheItem{ cacheable: cacheable, listElement: c.list.PushFront(key), } c.items[key] = item c.capcity -= cacheable.Size() return true}// 剔除不常用队缓存项。从队尾开始剔除。func (c *LRUCache) prune() { for i := 0; i < 50; i++ { tail := c.list.Back() if tail == nil { return } key := c.list.Remove(tail) item := c.items[key.(string)] delete(c.items, key.(string)) c.capcity += item.cacheable.Size() }}
到这里,LRUCache
算是实现完了。接下来体验一下。
type p struct {key string}type a struct {key string}func (p) Size() int { return 10}func (p p) Key() string { return p.key}func (a) Size() int { return 5}func (a a) Key() string { return a.key}func init() { rand.Seed(time.Now().UnixNano())}const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"func randstring(l int) string { r := make([]rune, l) for i := range r { r[i] = rune(letters[rand.Intn(len(letters))]) } return string(r)}func main() { p1 := p{key: randstring(6)} lc := New(30) fmt.Println(lc.get(p1.Key())) lc.set(p1) fmt.Println(lc.get(p1.Key())) p2 := p{key: randstring(6)} lc.set(p2) fmt.Println(lc.get(p2.Key())) p3 := p{key: randstring(6)} lc.set(p3) fmt.Println(lc.get(p3.Key())) fmt.Println(lc.items) fmt.Println(lc.list.Front().Value) lc.get(p1.Key()) fmt.Println(lc.list.Front().Value) a4 := a{key: randstring(4)} fmt.Println(lc.get(a4.Key())) lc.set(a4) fmt.Println(lc.items, lc.list.Len())}
这里查看
实际的使用其实还需要添加并发控制
。可以粗略地在get/set
函数里添加mutux
。当然最好是为 LRUCache
里的 hashtable
和 list
分别添加 read-write mutex
。
referce to:
发表评论
最新留言
网站不错 人气很旺了 加油
[***.192.178.218]2025年03月23日 20时04分38秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
wxpython的Hello,World代码探索
2021-05-08
【数字图像处理】OpenCV3 学习笔记
2021-05-08
【单片机开发】智能小车工程(经验总结)
2021-05-08
【单片机开发】基于stm32的掌上游戏机设计 (项目规划)
2021-05-08
KeepAlived介绍、配置示例、KeepAlived配置IPVS、调用脚本进行监控
2021-05-08
【Numpy学习】np.count_nonzero()用法解析
2021-05-08
Scala集合-数组、元组
2021-05-08
JAVA网络爬虫01-http client爬取网络内容
2021-05-08
04 程序流程控制
2021-05-08
java并发编程(1)
2021-05-08
C++&&STL
2021-05-08
子集(LeetCode 78)
2021-05-08
1004 Counting Leaves (30分)
2021-05-08
1093 Count PAT‘s (25分) 含DP做法
2021-05-08
一篇解决JMM与volatile详解(二)
2021-05-08
数据结构之数组与经典面试题(二)
2021-05-08
无锁并发框架-Disruptor的使用(二)
2021-05-08
Android wm命令
2021-05-08
boot.img 解包与打包
2021-05-08
Android4.4 平板背光设置
2021-05-08