LRU 最近最少使用演算法,LRU演算法主要用於快取淘汰。主要目的就是把最近最少使用的資料移除記憶體,以載入其他資料。
原理
新增元素時,放到連結串列頭
快取命中,將元素移動到連結串列頭
快取滿了之後,將連結串列尾的元素刪除
LRU演算法實現
- 可以用一個雙向連結串列儲存資料
- 使用hash實現O(1)的訪問
groupcache中LRU演算法實現(Go語言)
https://github.com/golang/groupcache/blob/master/lru/lru.go
原始碼簡單註釋:
package lru
import "container/list"
// Cache 結構體,定義lru cache 不是執行緒安全的
type Cache struct {
// 數目限制,0是無限制
MaxEntries int
// 刪除時, 可以新增可選的回撥函式
OnEvicted func(key Key, value interface{})
ll *list.List // 使用連結串列儲存資料
cache map[interface{}]*list.Element // map
}
// Key 是任何可以比較的值 http://golang.org/ref/spec#Comparison_operators
type Key interface{}
type entry struct {
key Key
value interface{}
}
// 建立新的cache 物件
func New(maxEntries int) *Cache {
return &Cache{
MaxEntries: maxEntries,
ll: list.New(),
cache: make(map[interface{}]*list.Element),
}
}
// 新增新的值到cache裡
func (c *Cache) Add(key Key, value interface{}) {
if c.cache == nil {
c.cache = make(map[interface{}]*list.Element)
c.ll = list.New()
}
if ee, ok := c.cache[key]; ok {
// 快取命中移動到連結串列的頭部
c.ll.MoveToFront(ee)
ee.Value.(*entry).value = value
return
}
// 新增資料到連結串列頭部
ele := c.ll.PushFront(&entry{key, value})
c.cache[key] = ele
if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
// 滿了刪除最後訪問的元素
c.RemoveOldest()
}
}
// 從cache裡獲取值.
func (c *Cache) Get(key Key) (value interface{}, ok bool) {
if c.cache == nil {
return
}
if ele, hit := c.cache[key]; hit {
// 快取命中,將命中元素移動到連結串列頭
c.ll.MoveToFront(ele)
return ele.Value.(*entry).value, true
}
return
}
// 刪除指定key的元素
func (c *Cache) Remove(key Key) {
if c.cache == nil {
return
}
if ele, hit := c.cache[key]; hit {
c.removeElement(ele)
}
}
// 刪除最後訪問的元素
func (c *Cache) RemoveOldest() {
if c.cache == nil {
return
}
ele := c.ll.Back()
if ele != nil {
c.removeElement(ele)
}
}
func (c *Cache) removeElement(e *list.Element) {
c.ll.Remove(e)
kv := e.Value.(*entry)
delete(c.cache, kv.key)
if c.OnEvicted != nil {
c.OnEvicted(kv.key, kv.value)
}
}
// cache 快取數
func (c *Cache) Len() int {
if c.cache == nil {
return 0
}
return c.ll.Len()
}