LRU的map+双链表实现(Go描述)

面云账户时候问了LRU,具体实现的方式是map+双链表。Set和Get的时间复杂度都是O(1)。完整写一遍复习一下, 仅作记录

/**
 * @Author: lzw5399
 * @Date: 2021/5/20 22:28
 * @Desc: 基于map和双链表实现的LRU算法
 */
package main

import "sync"

func main() {
	lru := NewLRUCache(3)

	lru.Set(1, 233)
	lru.Set(2, 666)
	lru.Set(3, 777)
	lru.Set(5, 888)

	lru.Get(2)
}

// LRUCache
type LRUCache struct {
	capacity int
	cache    map[int]*LinkedNode
	head     *LinkedNode
	tail     *LinkedNode
	sync.RWMutex
}

type LinkedNode struct {
	key, value int
	prev, next *LinkedNode
}

func NewLRUCache(capacity int) *LRUCache {
	return &LRUCache{
		capacity: capacity,
		cache:    make(map[int]*LinkedNode, capacity),
		head:     nil,
		tail:     nil,
		RWMutex:  sync.RWMutex{},
	}
}

// - key是否已存在
//   - 已存在, 将该节点移动到链表头部
//   - 未存在, 判断cap是否已满
//     - 满
//       - 移除链表尾的节点
//       - 新的node放入链表头
//       - 新的node放入cache的map中
//     - 未满
//       - 新的node放入链表头
//       - 新的node放入cache的map中
func (l *LRUCache) Set(key int, value int) {
	l.RLock()
	node, exist := l.cache[key]
	l.RUnlock()
	if exist {
		l.moveToHead(node)
		return
	}

	node = &LinkedNode{
		key:   key,
		value: value,
	}

	l.Lock()
	defer l.Unlock()

	if l.capacity == len(l.cache) {
		removedNode := l.removeTail()
		delete(l.cache, removedNode.key)
	}

	l.addToHead(node)
	l.cache[key] = node
}

// - 从map中获取是否存在
//   - 不存在
//     - 返回-1
//   - 存在
//     - 移到链表头部
//     - 并返回具体的值
func (l *LRUCache) Get(key int) int {
	l.RLock()
	node, exist := l.cache[key]
	l.RUnlock()
	if !exist {
		return -1
	}

	l.moveToHead(node)
	return node.value
}

func (l *LRUCache) moveToHead(node *LinkedNode) {
	l.removeNode(node)
	l.addToHead(node)
}

func (l *LRUCache) removeTail() *LinkedNode {
	return l.removeNode(l.tail)
}

func (l *LRUCache) removeNode(node *LinkedNode) *LinkedNode {
	// 头节点
	if node.prev == nil {
		l.head = node.next
		node.next.prev = nil
		return node
	}

	// 尾节点
	if node.next == nil {
		l.tail = node.prev
		node.prev.next = nil
		return node
	}

	// 中间节点
	node.prev.next = node.next
	node.next.prev = node.prev
	return node
}

func (l *LRUCache) addToHead(node *LinkedNode) {
	if l.head != nil {
		l.head.prev = node
	} else {
		l.tail = node
	}

	node.prev = nil
	node.next = l.head
	l.head = node
}

上一篇:厉害了,自己动手实现 LRU 缓存机制!


下一篇:Redis 缓存替换策略