mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
test: add examples for lrucache
This commit is contained in:
121
algorithm/lrucache.go
Normal file
121
algorithm/lrucache.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
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 (thread unsafe)
|
||||||
|
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 creates a LRUCache pointer instance.
|
||||||
|
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] {
|
||||||
|
return &LRUCache[K, V]{
|
||||||
|
cache: make(map[K]*lruNode[K, V], capacity),
|
||||||
|
head: nil,
|
||||||
|
tail: nil,
|
||||||
|
capacity: capacity,
|
||||||
|
length: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get value of key from lru cache.
|
||||||
|
// Play: https://go.dev/play/p/iUynEfOP8G0
|
||||||
|
func (l *LRUCache[K, V]) Get(key K) (V, bool) {
|
||||||
|
var value V
|
||||||
|
|
||||||
|
node, ok := l.cache[key]
|
||||||
|
if ok {
|
||||||
|
l.moveToHead(node)
|
||||||
|
return node.value, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put value of key into lru cache.
|
||||||
|
// Play: https://go.dev/play/p/iUynEfOP8G0
|
||||||
|
func (l *LRUCache[K, V]) Put(key K, value V) {
|
||||||
|
node, ok := l.cache[key]
|
||||||
|
if !ok {
|
||||||
|
newNode := newLruNode(key, value)
|
||||||
|
l.cache[key] = newNode
|
||||||
|
l.addNode(newNode)
|
||||||
|
|
||||||
|
if len(l.cache) > l.capacity {
|
||||||
|
oldKey := l.deleteNode(l.head)
|
||||||
|
delete(l.cache, oldKey)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.value = value
|
||||||
|
l.moveToHead(node)
|
||||||
|
}
|
||||||
|
l.length = len(l.cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete item from lru cache.
|
||||||
|
func (l *LRUCache[K, V]) Delete(key K) bool {
|
||||||
|
node, ok := l.cache[key]
|
||||||
|
if ok {
|
||||||
|
key := l.deleteNode(node)
|
||||||
|
delete(l.cache, key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the cache.
|
||||||
|
func (l *LRUCache[K, V]) Len() int {
|
||||||
|
return l.length
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LRUCache[K, V]) addNode(node *lruNode[K, V]) {
|
||||||
|
if l.tail != nil {
|
||||||
|
l.tail.next = node
|
||||||
|
node.pre = l.tail
|
||||||
|
node.next = nil
|
||||||
|
}
|
||||||
|
l.tail = node
|
||||||
|
if l.head == nil {
|
||||||
|
l.head = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LRUCache[K, V]) deleteNode(node *lruNode[K, V]) K {
|
||||||
|
if node == l.tail {
|
||||||
|
l.tail = l.tail.pre
|
||||||
|
} else if node == l.head {
|
||||||
|
l.head = l.head.next
|
||||||
|
} else {
|
||||||
|
node.pre.next = node.next
|
||||||
|
node.next.pre = node.pre
|
||||||
|
}
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LRUCache[K, V]) moveToHead(node *lruNode[K, V]) {
|
||||||
|
if l.tail == node {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.deleteNode(node)
|
||||||
|
l.addNode(node)
|
||||||
|
}
|
||||||
79
algorithm/lrucache_example_test.go
Normal file
79
algorithm/lrucache_example_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package algorithm
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func ExampleLRUCache_Put() {
|
||||||
|
cache := NewLRUCache[int, int](2)
|
||||||
|
|
||||||
|
cache.Put(1, 1)
|
||||||
|
cache.Put(2, 2)
|
||||||
|
|
||||||
|
result1, ok1 := cache.Get(1)
|
||||||
|
result2, ok2 := cache.Get(2)
|
||||||
|
result3, ok3 := cache.Get(3)
|
||||||
|
|
||||||
|
fmt.Println(result1, ok1)
|
||||||
|
fmt.Println(result2, ok2)
|
||||||
|
fmt.Println(result3, ok3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1 true
|
||||||
|
// 2 true
|
||||||
|
// 0 false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleLRUCache_Get() {
|
||||||
|
cache := NewLRUCache[int, int](2)
|
||||||
|
|
||||||
|
cache.Put(1, 1)
|
||||||
|
cache.Put(2, 2)
|
||||||
|
|
||||||
|
result1, ok1 := cache.Get(1)
|
||||||
|
result2, ok2 := cache.Get(2)
|
||||||
|
result3, ok3 := cache.Get(3)
|
||||||
|
|
||||||
|
fmt.Println(result1, ok1)
|
||||||
|
fmt.Println(result2, ok2)
|
||||||
|
fmt.Println(result3, ok3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1 true
|
||||||
|
// 2 true
|
||||||
|
// 0 false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleLRUCache_Delete() {
|
||||||
|
cache := NewLRUCache[int, int](2)
|
||||||
|
|
||||||
|
cache.Put(1, 1)
|
||||||
|
cache.Put(2, 2)
|
||||||
|
|
||||||
|
result1, ok1 := cache.Get(1)
|
||||||
|
|
||||||
|
ok2 := cache.Delete(2)
|
||||||
|
|
||||||
|
_, ok3 := cache.Get(2)
|
||||||
|
|
||||||
|
fmt.Println(result1, ok1)
|
||||||
|
fmt.Println(ok2)
|
||||||
|
fmt.Println(ok3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1 true
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleLRUCache_Len() {
|
||||||
|
cache := NewLRUCache[int, int](2)
|
||||||
|
|
||||||
|
cache.Put(1, 1)
|
||||||
|
cache.Put(2, 2)
|
||||||
|
|
||||||
|
result := cache.Len()
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
||||||
33
algorithm/lrucache_test.go
Normal file
33
algorithm/lrucache_test.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package algorithm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLRUCache(t *testing.T) {
|
||||||
|
asssert := internal.NewAssert(t, "TestLRUCache")
|
||||||
|
|
||||||
|
cache := NewLRUCache[int, int](3)
|
||||||
|
|
||||||
|
cache.Put(1, 1)
|
||||||
|
cache.Put(2, 2)
|
||||||
|
cache.Put(3, 3)
|
||||||
|
|
||||||
|
asssert.Equal(3, cache.Len())
|
||||||
|
|
||||||
|
v, ok := cache.Get(1)
|
||||||
|
asssert.Equal(true, ok)
|
||||||
|
asssert.Equal(1, v)
|
||||||
|
|
||||||
|
v, ok = cache.Get(2)
|
||||||
|
asssert.Equal(true, ok)
|
||||||
|
asssert.Equal(2, v)
|
||||||
|
|
||||||
|
ok = cache.Delete(2)
|
||||||
|
asssert.Equal(true, ok)
|
||||||
|
|
||||||
|
_, ok = cache.Get(2)
|
||||||
|
asssert.Equal(false, ok)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user