实现一个线程安全并且可以设置过期时间的LRU(LinkedHashMap原理)

目录

1、HashMap原理

2、LinkedHashMap实现LRU原理(accessOrder = true)

2.1 数据结构

2.2 put方法

2.3 get方法

2.4 remove方法

3、普通LRU代码实现

4、实现一个线程安全并且可以设置过期时间的LRU缓存

4.1 解决安全问题

4.2 实现定期删除


FIFO的思想是实现一个先进先出的队列,LRU最近最久未使用。可以用双向链表linkedList来实现,同时为了兼顾查询节点时的效率,结合HashMap来实现。双向链表linkedList+HashMap的数据结构可以联想到LinkedHashMap,就不需要我们自己来实现了。LinkedHashMap存储数据是有序的,可以分为插入顺序(accessOrder = false)和访问顺序(accessOrder = true),默认为插入顺序,而且LinkedHashMap提供了删除最后一个节点的方法removeEldestEntry(Map.Entry eldest),正好可以用来实现FIFO(LinkedHashMap按插入顺序存储数据)和LRU算法(LinkedHashMap按访问顺序存储数据)。

1、HashMap原理

底层是Entry数组+链表(Entry节点的next指针)+红黑树。JDK8中,链表长度不小于8时,将链表转化为红黑树。默认无参构造函数会初始化大小为16,向集合中添加元素至集合大小的0.75倍时,会生成一个大小为原来2倍的新集合,然后重新计算元素的地址,将集合中元素插入到新集合,届时效率很低。线程不安全。(例如:put的时候导致的数据覆盖、集合扩展时(resize方法)会出现死循环)。

//HashMap的Entry结构:
static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
}

2、LinkedHashMap实现LRU原理(accessOrder = true

  • 2.1 数据结构

HashMap的原理是内部维护了一个Entry数组,而LinkedHashMap在HashMap的基础上,增加了链表头节点和尾节点两个指针,增加了排序方式的标志,Entry节点增加了前后两个指针。因此LinkedHashMap的Entry节点有三个指针,一个是双向链表的前指针、一个是双向链表的后指针、一个是HashMap的hash地址重复时拉链法解决冲突的next的指针。

/*LinkedHashMap的Entry结构:*/
//双向链表头结点
transient LinkedHashMap.Entry<K,V> head;
//双向链表尾节点
transient LinkedHashMap.Entry<K,V> tail;
//是否基于访问顺序排序(默认为false即插入顺序排序)
final boolean accessOrder;
//Entry继承了HashMap的Entry,又增加了before, after两个指针
private static class Entry<K,V> extends HashMap.Entry<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }
}
  • 2.2 put方法

如果put的是新key,则将Entry节点添加到Map中,并添加到双向链表的尾部,若initialCapacity数量已满,删除最近最久未使用的Entry节点即双向链表的头结点;若put的是已有的key,更新节点的value,并将节点删除并添加到尾部。

HashMap的put方法会生成一个节点,调用了newNode方法,而Lin
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮您写一个Go语言的LRU缓存,以下是示例代码: ``` package main import ( "container/list" "fmt" "time" ) type CacheItem struct { key string value interface{} expired_at time.Time } type LRUCache struct { capacity int items map[string]*list.Element list *list.List } func NewLRUCache(capacity int) *LRUCache { return &LRUCache{ capacity: capacity, items: make(map[string]*list.Element), list: list.New(), } } func (c *LRUCache) Get(key string) (interface{}, bool) { if elem, ok := c.items[key]; ok { item := elem.Value.(*CacheItem) if item.expired_at.IsZero() || item.expired_at.After(time.Now()) { c.list.MoveToFront(elem) return item.value, true } else { c.list.Remove(elem) delete(c.items, key) } } return nil, false } func (c *LRUCache) Set(key string, value interface{}, expired time.Duration) { if elem, ok := c.items[key]; ok { c.list.MoveToFront(elem) elem.Value.(*CacheItem).value = value elem.Value.(*CacheItem).expired_at = time.Now().Add(expired) } else { if len(c.items) >= c.capacity { tail := c.list.Back() if tail != nil { delete(c.items, tail.Value.(*CacheItem).key) c.list.Remove(tail) } } elem := c.list.PushFront(&CacheItem{key, value, time.Now().Add(expired)}) c.items[key] = elem } } func main() { cache := NewLRUCache(2) cache.Set("key1", "value1", time.Second*5) cache.Set("key2", "value2", time.Second*10) cache.Set("key3", "value3", time.Second*15) if value, ok := cache.Get("key1"); ok { fmt.Println("key1:", value) } else { fmt.Println("key1 not found") } if value, ok := cache.Get("key2"); ok { fmt.Println("key2:", value) } else { fmt.Println("key2 not found") } if value, ok := cache.Get("key3"); ok { fmt.Println("key3:", value) } else { fmt.Println("key3 not found") } } ``` 这个LRU缓存可以设置过期时间,当缓存中的数据过期时,会自动删除。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值