定义一个接口,提供缓存操作的系列方法,如下
type ICache interface {
Get(key string) (any, bool)
Set(key string, value any)
Delete(key string)
Len() int
Cap() int
Keys() []string
Values() []any
Items() map[string]any
Clear()
LList() []any
RList() []any
}
定义节点结构,用于保存缓存的key、value、前级节点、后级节点和最后访问时间,具体实现如下
type node struct {
key string
value any
pre *node
next *node
visitedAt time.Time
}
定义一个cache结构,用map+双向list,map提供接近O(1)的缓存查找效率,双向list提供O(1)的调整效率,head和tail分别指向list的头节点和尾结点,sync.Mutex用以保证线程安全
type cache struct {
dataPtr map[string]*node
head *node
tail *node
cap int
sync.Mutex
}
三个涉及到节点调整的操作,分别是Get、Set和Delete
- Get从map中拿到节点,如果节点不是头节点,则要将该节点提升到头节点,表示该节点最近被访问过
- Set将内容存入缓存,如果存在key对应的节点,更新其值并提升为头节点,如果不存在则创建一个新的,如果缓存满了,就删除tail节点,并将新节点插入head
- Delete删除一个缓存对象,如果是头节点,则头节点指针后移一个节点,如果是尾节点,则尾节点指针 前移一个节点
将当前节点提升为头节点的代码如下:
func (c *cache) setNodeToFirst(n *node) {
pre := n.pre
next := n.next
// 将当前节点插入头节点位置
n.pre = nil
n.next = c.head
c.head.pre = n
c.head = n
// 将当前节点从原来位置摘除
if pre != nil {
pre.next = next
}
if next != nil {
next.pre = pre
}
// 如果该节点是尾节点,则将尾结点指针重新指向新的尾结点
if n == c.tail {
if pre != nil {
c.tail = pre
} else {
c.tail = c.head
}
}
}
完整代码如下
package cache
import (
"sync"
"time"
)
type ICache interface {
Get(key string) (any, bool)
Set(key string, value any)
Delete(key string)
Len() int
Cap() int
Keys() []string
Values() []any
Items() map[string]any
Clear()
LList() []any
RList() []any
}
func New(cap int) ICache {
return newCache(cap)
}
type node struct {
key string
value any
pre *node
next *node
visitedAt time.Time
}
func newNode(key string, value any) *node {
return &node{
key: key,
value: value,
visitedAt: time.Now(),
}
}
type cache struct {
dataPtr map[string]*node
head *node
tail *node
cap int
sync.Mutex
}
func newCache(cap int) *cache {
if cap <= 0 {
panic("cache capacity must be greater than 0")
}
return &cache{
dataPtr: make(map[string]*node, cap),
cap: cap,
}
}
func (c *cache) Get(key string) (any, bool) {
c.Lock()
defer c.Unlock()
n := c.dataPtr[key]
if n == nil {
return nil, false
}
n.visitedAt = time.Now()
if n == c.head {
return n.value, true
}
c.setNodeToFirst(n)
return n.value, true
}
func (c *cache) Set(key string, value any) {
c.Lock()
defer c.Unlock()
if n := c.dataPtr[key]; n != nil {
n.value = value
n.visitedAt = time.Now()
if n != c.head {
c.setNodeToFirst(n)
}
return
}
if len(c.dataPtr) >= c.cap {
delete(c.dataPtr, c.tail.key)
c.tail = c.tail.pre
if c.tail != nil {
c.tail.next = nil
}
}
n := newNode(key, value)
c.dataPtr[key] = n
if c.head == nil {
c.head = n
c.tail = n
} else {
c.head.pre = n
n.next = c.head
c.head = n
}
}
func (c *cache) setNodeToFirst(n *node) {
pre := n.pre
next := n.next
n.pre = nil
n.next = c.head
c.head.pre = n
c.head = n
if pre != nil {
pre.next = next
}
if next != nil {
next.pre = pre
}
if n == c.tail {
if pre != nil {
c.tail = pre
} else {
c.tail = c.head
}
}
}
func (c *cache) Delete(key string) {
c.Lock()
defer c.Unlock()
n := c.dataPtr[key]
if n == nil {
return
}
delete(c.dataPtr, key)
if n.pre != nil {
n.pre.next = n.next
} else {
c.head = n.next
}
if n.next != nil {
n.next.pre = n.pre
} else {
c.tail = n.pre
}
}
func (c *cache) LList() []any {
c.Lock()
defer c.Unlock()
list := make([]any, 0, len(c.dataPtr))
for n := c.head; n != nil; n = n.next {
list = append(list, n.value)
}
return list
}
func (c *cache) RList() []any {
c.Lock()
defer c.Unlock()
list := make([]any, 0, len(c.dataPtr))
for n := c.tail; n != nil; n = n.pre {
list = append(list, n.value)
}
return list
}
func (c *cache) Items() map[string]any {
c.Lock()
defer c.Unlock()
items := make(map[string]any, len(c.dataPtr))
for k, n := range c.dataPtr {
items[k] = n.value
}
return items
}
func (c *cache) Keys() []string {
c.Lock()
defer c.Unlock()
keys := make([]string, 0, len(c.dataPtr))
for k := range c.dataPtr {
keys = append(keys, k)
}
return keys
}
func (c *cache) Values() []any {
c.Lock()
defer c.Unlock()
values := make([]any, 0, len(c.dataPtr))
for _, n := range c.dataPtr {
values = append(values, n.value)
}
return values
}
func (c *cache) Len() int {
c.Lock()
defer c.Unlock()
return len(c.dataPtr)
}
func (c *cache) Cap() int {
c.Lock()
defer c.Unlock()
return c.cap
}
func (c *cache) Clear() {
c.Lock()
defer c.Unlock()
c.dataPtr = make(map[string]*node, c.cap)
c.head = nil
c.tail = nil
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。