MST

星途 面试题库

面试题:Go空接口在实现通用数据结构中的应用优化

假设你要设计一个通用的缓存结构,使用空接口来存储不同类型的数据。请描述如何在保证类型安全的前提下,优化该缓存结构的性能,例如减少类型断言的开销,并且说明如何处理缓存数据的过期和淘汰策略,用Go代码简单示意关键部分。
38.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

减少类型断言开销

  1. 类型参数(Go 1.18+):使用泛型来减少类型断言。定义缓存时,使用类型参数指定缓存数据的类型,这样在获取和设置数据时不需要进行类型断言。
  2. 接口方法约束:如果使用空接口,通过定义接口方法来约束缓存数据的行为,在获取数据时,可以直接调用接口方法,减少不必要的类型断言。

缓存数据过期和淘汰策略

  1. 过期策略:在缓存数据结构中添加一个过期时间字段,每次获取数据时检查当前时间是否超过过期时间,超过则删除该数据。
  2. 淘汰策略:常见的淘汰策略有LRU(最近最少使用)、LFU(最不经常使用)等。可以通过维护一个链表或使用哈希表结合链表的方式实现LRU,对于LFU可以使用哈希表记录每个数据的访问频率。

Go代码示例

package main

import (
    "container/list"
    "fmt"
    "time"
)

// 定义缓存项结构
type CacheItem struct {
    key        string
    value      interface{}
    expiration time.Time
}

// 定义缓存结构
type Cache struct {
    items       map[string]*list.Element
    lruList     *list.List
    capacity    int
    expiration  time.Duration
}

// 创建新的缓存
func NewCache(capacity int, expiration time.Duration) *Cache {
    return &Cache{
        items:       make(map[string]*list.Element),
        lruList:     list.New(),
        capacity:    capacity,
        expiration:  expiration,
    }
}

// 设置缓存
func (c *Cache) Set(key string, value interface{}) {
    if elem, exists := c.items[key]; exists {
        c.lruList.MoveToFront(elem)
        elem.Value.(*CacheItem).value = value
        elem.Value.(*CacheItem).expiration = time.Now().Add(c.expiration)
        return
    }

    item := &CacheItem{
        key:        key,
        value:      value,
        expiration: time.Now().Add(c.expiration),
    }
    elem := c.lruList.PushFront(item)
    c.items[key] = elem

    if c.lruList.Len() > c.capacity {
        c.removeOldest()
    }
}

// 获取缓存
func (c *Cache) Get(key string) (interface{}, bool) {
    if elem, exists := c.items[key]; exists {
        item := elem.Value.(*CacheItem)
        if time.Now().After(item.expiration) {
            c.remove(elem)
            return nil, false
        }
        c.lruList.MoveToFront(elem)
        return item.value, true
    }
    return nil, false
}

// 删除最久未使用的项
func (c *Cache) removeOldest() {
    elem := c.lruList.Back()
    if elem != nil {
        c.remove(elem)
    }
}

// 删除指定项
func (c *Cache) remove(elem *list.Element) {
    c.lruList.Remove(elem)
    item := elem.Value.(*CacheItem)
    delete(c.items, item.key)
}

你可以这样使用上述缓存:

func main() {
    cache := NewCache(2, 2*time.Second)
    cache.Set("key1", "value1")
    cache.Set("key2", "value2")

    value, exists := cache.Get("key1")
    if exists {
        fmt.Println("Value for key1:", value)
    }

    cache.Set("key3", "value3")
    _, exists = cache.Get("key2")
    if!exists {
        fmt.Println("Key2 was evicted")
    }

    time.Sleep(3 * time.Second)
    _, exists = cache.Get("key1")
    if!exists {
        fmt.Println("Key1 has expired")
    }
}

此代码实现了一个简单的带有过期和LRU淘汰策略的缓存。Set 方法用于设置缓存项,Get 方法用于获取缓存项并处理过期,removeOldest 方法实现了LRU淘汰策略。