From 4fc1cae2dac77b5754325def35aef5cf360f13a7 Mon Sep 17 00:00:00 2001 From: old-tom <892955278@msn.cn> Date: Sun, 24 Dec 2023 19:13:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20LRU=E7=BC=93=E5=AD=98=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 23 +++++++++++++++++++++++ lfu/lfu.go | 13 ++++++++++--- lfu/lfu_test.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 lfu/lfu_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78f591a --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +### Go template +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + diff --git a/lfu/lfu.go b/lfu/lfu.go index 11aa377..c032050 100644 --- a/lfu/lfu.go +++ b/lfu/lfu.go @@ -9,6 +9,7 @@ import ( // @Description:LFU,即最少使用,也就是淘汰缓存中访问频率最低的记录。 // LFU 认为,如果数据过去被访问多次,那么将来被访问的频率也更高。 // LFU 的实现需要维护一个按照访问次数排序的队列,每次访问,访问次数加 1,队列重新排序,淘汰时选择访问次数最少的即可 +// 利用最小堆属性对访问次数排序实现 type lfu struct { // 缓存最大容量 maxBytes int @@ -41,7 +42,7 @@ func (l *lfu) Set(key string, value any) { l.cache[key] = en l.usedBytes += en.Len() if l.maxBytes > 0 && l.usedBytes > l.maxBytes { - + l.removeElement(heap.Pop(l.queue)) } } } @@ -61,11 +62,14 @@ func (l *lfu) Del(Key string) { } func (l *lfu) DelOldest() { - + if l.queue.Len() == 0 { + return + } + l.removeElement(heap.Pop(l.queue)) } func (l *lfu) Len() int { - return 0 + return l.queue.Len() } func (l *lfu) removeElement(x any) { @@ -75,4 +79,7 @@ func (l *lfu) removeElement(x any) { en := x.(*entry) delete(l.cache, en.key) l.usedBytes -= en.Len() + if l.onEvicted != nil { + l.onEvicted(en.key, en.value) + } } diff --git a/lfu/lfu_test.go b/lfu/lfu_test.go new file mode 100644 index 0000000..0b313d2 --- /dev/null +++ b/lfu/lfu_test.go @@ -0,0 +1,34 @@ +package lfu_test + +import ( + "cache/lfu" + "github.com/matryer/is" + "testing" +) + +func TestSet(t *testing.T) { + is := is.New(t) + cache := lfu.New(24, nil) + cache.DelOldest() + cache.Set("k1", 1) + v := cache.Get("k1") + is.Equal(v, 1) + cache.Del("k1") + is.Equal(0, cache.Len()) +} + +func TestOnEvicted(t *testing.T) { + is := is.New(t) + keys := make([]string, 0, 8) + onEvicted := func(key string, value interface{}) { + keys = append(keys, key) + } + cache := lfu.New(32, onEvicted) + cache.Set("k1", 1) + cache.Set("k2", 2) + cache.Set("k3", 3) + cache.Set("k4", 4) + expected := []string{"k1", "k3"} + is.Equal(expected, keys) + is.Equal(2, cache.Len()) +}