定义一个接口,提供缓存操作的系列方法,如下

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

  1. Get从map中拿到节点,如果节点不是头节点,则要将该节点提升到头节点,表示该节点最近被访问过
  2. Set将内容存入缓存,如果存在key对应的节点,更新其值并提升为头节点,如果不存在则创建一个新的,如果缓存满了,就删除tail节点,并将新节点插入head
  3. 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
}

JackLuo_Gopher
1 声望0 粉丝