From 78519088ab15b2a4209414f51e1b180ac18a0caf Mon Sep 17 00:00:00 2001 From: dudaodong Date: Fri, 11 Mar 2022 17:05:55 +0800 Subject: [PATCH] feat: add lru cache --- algorithm/lru_cache.go | 99 +++++++++++++++++++++++++++++++++++++ algorithm/lru_cache_test.go | 19 +++++++ 2 files changed, 118 insertions(+) create mode 100644 algorithm/lru_cache.go create mode 100644 algorithm/lru_cache_test.go diff --git a/algorithm/lru_cache.go b/algorithm/lru_cache.go new file mode 100644 index 0000000..84e39a0 --- /dev/null +++ b/algorithm/lru_cache.go @@ -0,0 +1,99 @@ +package algorithm + +type lruNode[K comparable, V any] struct { + key K + value V + pre *lruNode[K, V] + next *lruNode[K, V] +} + +// newLruNode return a lruNode pointer +func newLruNode[K comparable, V any](key K, value V) *lruNode[K, V] { + return &lruNode[K, V]{ + key: key, + value: value, + pre: nil, + next: nil, + } +} + +// LRUCache lru cache +type LRUCache[K comparable, V any] struct { + cache map[K]*lruNode[K, V] + head *lruNode[K, V] + tail *lruNode[K, V] + capacity int + length int +} + +// NewLRUCache return a LRUCache pointer +func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] { + return &LRUCache[K, V]{ + cache: make(map[K]*lruNode[K, V]), + head: nil, + tail: nil, + capacity: capacity, + length: 0, + } +} + +// Get value of key from lru cache +func (l *LRUCache[K, V]) Get(key K) (V, bool) { + var value V + + node, ok := l.cache[key] + if node == nil || !ok { + return value, false + } + + l.moveToHead(node) + + return node.value, true +} + +// Put value of key into lru cache +func (l *LRUCache[K, V]) Put(key K, value V) { + node, ok := l.cache[key] + if node == nil || !ok { + newNode := newLruNode(key, value) + l.cache[key] = newNode + l.addNode(newNode) + + l.length++ + if l.length > l.capacity { + tail := l.popTail() + delete(l.cache, tail.key) + l.length-- + } + } + + node.value = value + l.moveToHead(node) +} + +func (l *LRUCache[K, V]) addNode(node *lruNode[K, V]) { + node.pre = l.head + node.next = l.head.next + + l.head.next.pre = node + l.head.next = node +} + +func (l *LRUCache[K, V]) deleteNode(node *lruNode[K, V]) { + pre := node.pre + next := node.next + + pre.next = next + next.pre = pre +} + +func (l *LRUCache[K, V]) moveToHead(node *lruNode[K, V]) { + l.deleteNode(node) + l.addNode(node) +} + +func (l *LRUCache[K, V]) popTail() *lruNode[K, V] { + node := l.tail.pre + l.deleteNode(node) + return node +} diff --git a/algorithm/lru_cache_test.go b/algorithm/lru_cache_test.go new file mode 100644 index 0000000..a4ab054 --- /dev/null +++ b/algorithm/lru_cache_test.go @@ -0,0 +1,19 @@ +package algorithm + +import ( + "testing" + + "github.com/duke-git/lancet/internal" +) + +func TestLRUCache(t *testing.T) { + asssert := internal.NewAssert(t, "TestLRUCache") + + cache := NewLRUCache[int, int](2) + + cache.Put(1, 1) + cache.Put(2, 2) + + _, ok := cache.Get(0) + asssert.Equal(false, ok) +}