Go语言LRU算法

251 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情

package main

import (
	"fmt"
)

// 定义节点结构体
type node struct {
	key   string
	value string
	next  *node
	prev  *node
}

// 定义lru结构体
type lru struct {
	//缓存最大数量
	k int
	// 存放节点数据
	cache map[string]*node
	//头节点
	head *node
	// 尾节点
	tail *node
}

// 工厂方法,构造一个LRU缓存对象
func NewLRUCache(k int) *lru {
	return &lru{k: k}
}

// 对外开放的Get方法
func (lru *lru) Get(key string) string {
	if lru.cache == nil {
		return ""
	}
	n, ok := lru.cache[key]
	if ok {
		value := n.value
		//更新到头节点
		lru.moveNodeToHead(n)
		return value
	} else {
		return ""
	}
}

// 对外开放的Set方法
func (lru *lru) Set(key string, value string) {
	//初始化map
	if lru.cache == nil {
		lru.cache = make(map[string]*node)
	}
	n, ok := lru.cache[key]
	if ok {
		//更新缓存
		n.value = value
	} else {
		//创建新节点
		n = &node{key: key, value: value}
		// 添加到map中
		lru.cache[key] = n
		//如果超过缓存大小,移除最后一个节点
		if len(lru.cache) > lru.k {
			lru.removeTail()
		}
	}
	// 更新节点为头节点
	lru.moveNodeToHead(n)
}

//将指定节点移到头节点
func (lru *lru) moveNodeToHead(n *node) {
	//如果头节点和尾节点都是空,说明是第一次添加数据
	if lru.head == nil && lru.tail == nil {
		lru.head = n
		lru.tail = n
		return
	}
	// 如果是头节点,那么不动
	if lru.head == n {
		return
	}
	nextNode := n.next
	prevNode := n.prev
	// 如果是尾节点,那么要先与上一个节点断开
	if n == lru.tail {
		prevNode.next = nil
		n.prev = nil
	} else if prevNode != nil && nextNode != nil {
		//如果是中间节点,要与前后节点都断开
		nextNode.prev = prevNode
		prevNode.next = nextNode
		n.prev = nil
	}
	//把节点移到到头节点
	n.next = lru.head
	lru.head.prev = n
	lru.head = n
}

//移除最后一个节点
func (lru *lru) removeTail() {
	// 如果尾节点wei空,那么清空数据直接返回
	if lru.tail == nil {
		lru.cache = nil
		return
	}
	tailPrev := lru.tail.prev
	// 尾节点与上一个节点断开
	lru.tail.prev = nil
	if tailPrev != nil {
		tailPrev.next = nil
	}
	// 把上一个节点置为尾节点
	lru.tail = tailPrev
	// 从map中删除尾节点数据
	delete(lru.cache, lru.tail.key)
}

// 重写String方法,自定义打印结果
func (lru *lru) String() string {
	point := lru.head
	result := ""
	flag := "\t"
	for point != nil {
		if len(result) > 0 {
			result = fmt.Sprintf("%v%v[%v:%v]", result, flag, point.key, point.value)
		} else {
			result = fmt.Sprintf("[%v:%v]", point.key, point.value)
		}
		point = point.next
	}
	return result
}

func main() {
	LRUCache := NewLRUCache(5)
	LRUCache.Set("1", "1")
	LRUCache.Set("2", "2")
	LRUCache.Set("3", "3")
	LRUCache.Set("4", "4")
	LRUCache.Set("5", "5")
	//打印缓存数据,验证容量是否起作用
	fmt.Println(LRUCache)
	//设置超过最大容量的数据
	LRUCache.Set("6", "6")
	//打印缓存数据,验证超过容量是否起作用
	fmt.Println(LRUCache)
	//查询头节点数据
	fmt.Println(LRUCache.Get("6"))
	//打印缓存数据
	fmt.Println(LRUCache)
	//查询非头节点数据
	fmt.Println(LRUCache.Get("4"))
	//打印缓存数据
	fmt.Println(LRUCache)
	//查询尾节点数据
	fmt.Println(LRUCache.Get("2"))
	//打印缓存数据
	fmt.Println(LRUCache)
}

image-20210319141154317.png