java双链表实现lru算法_golang LRU实现(使用双向链表实现)

LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

lru.go

type Key interface{}

type entry struct {

key Key

value interface{}

}

type Cache Struct {

MaxEntries int

OnEvicted func(key Key,value interface{})

ll *list.List

cache map[interface{}]*list.Element

}

定义了Key,entry,Cache类型,

Key:interface{}类型,在Go中interface{}可以指向任意对象,也就任意对象都是先这个接口,你可以把它看成Java/C#中的Object,C/C++中的void*。

Cache:MaxEntires表示Cache中最大可以有的KV数,OnEvicted是一个回调函数,在触发淘汰时调用,ll是一个链表,cache是一个map,key为interface{}, value为list.Element其指向的类型是entry类型。

通过数据结构我们可以猜到LRU的实现,Get一次放到list头,每次Add的时候判断是否满,满则淘汰掉list尾的数据

// 创建一个Cache

func New(maxEntries int) *Cache {

return &Cache{

MaxEntries: maxEntries,ll: list.New(),cache: make(map[interface{}]*list.Element),}

}

// 向Cache中插入一个KV,如果缓存已满,则删除最久为使用的对象。

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中获取一个key对应的value,并把key移动到列表的开头。表示最近使用过。

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

}

// 从Cache中删除一个key

func (c *Cache) Remove(key Key) {

if c.cache == nil {

return

}

if ele,hit := c.cache[key]; hit {

c.removeElement(ele)

}

}

// 从Cache中删除最久未被访问的数据

func (c *Cache) RemoveOldest() {

if c.cache == nil {

return

}

ele := c.ll.Back()

if ele != nil {

c.removeElement(ele)

}

}

// 获取当前Cache中的元素个数

func (c *Cache) Len() int {

if c.cache == nil {

return 0

}

return c.ll.Len()

}

//删除缓存中的元素,私有方法

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)

}

}

这里我们看到了Go中如何实现一个类的成员方法,在func之后加类,在Go中接口的实现并不是和Java中那样子,而是只要某个类只要实现了某个接口的所有方法,即可认为该类实现了该接口。

类似的比如说在java中有2个接口名字不同,即使方法相同也是不一样的,而Go里面认为这是一样的。

另外Go中开头字母大写的变量名,类名,方法名表示对外可知,小写开头的表示对外不暴露。

另外类实这种代码ele.Value.(*entry).value,其中(*entry)表示将Value转成*entry类型访问。

相关文章

总结

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。

如您喜欢交流学习经验,点击链接加入交流1群:1065694478(已满)交流2群:163560250

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值