前言

  最近复习操作系统,看到了lru算法,就去网上搜索下,因此发现了GeeCache,顺手写了一遍。研究下lru算法的实现。

正文:

  lru使用map+链表实现。map里面存储了key以及其对应的链表节点。当我们根据某个key访问缓存值的时候,可以经过map快速定位到该链表节点。从而获取值

下面我们来看下它的具体实现:

首先,我们可以考虑下lru的结构:

  1.map

  2.链表

  3.占用的内存大小

  4.最大内存

type Cache struct {
	maxBytes  int64
	nbytes    int64
	ll     *list.List
	cache  map[string]*list.Element
}

添加key:

lru算法的思想是经常访问的元素移动到队头,不常访问的元素移动到队尾。从而进行淘汰队尾元素。

当我们添加的key已经存在的时候,我们只需要更新其对应的值即可。

不存在的时候,需要插入链表和map

如果内存已经不够,我们需要开启淘汰算法

func (c *Cache) Add(key string,value Value)  {
	if v,ok := c.cache[key]; ok{
	//	 移动到常用元素 --> 队头
		c.ll.MoveToFront(v)
	//   获取旧值
		kv := v.Value.(*entry)
		c.nbytes += int64(value.Len()) - int64(kv.value.Len())
	// 更新值
		kv.value = value
	}else{
		ele := c.ll.PushFront(&entry{key,value})
		c.cache[key] = ele
		c.nbytes += int64(len(key)) + int64(value.Len())
	}
	if c.maxBytes != 0 || c.maxBytes < c.nbytes{
    //  淘汰算法
		c.RemoveOldest()
	}
}

  下面我们来看淘汰算法:

    在链表中移除队尾的元素,删除map中相应的key

func (c *Cache) RemoveOldest()  {
    ele := c.ll.Back()
    if ele != nil {
        c.ll.Remove(ele)
        kv := ele.Value.(*entry)
        delete(c.cache,kv.key)
        c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len())
    }
}

根据key获取缓存中元素就简单了,根据map定位到链表元素即可:

func (c *Cache) Get(key string)(value Value,ok bool)  {
	if ele,ok := c.cache[key];ok {
		c.ll.PushFront(ele)
		kv := ele.Value.(entry)
		return kv.value,true
	}
	return
}

  

lru算法在GeeCache中的实现还是挺好理解的。记录下~

 

| 不骄不躁,保持学习