C# 实现Lru缓存
LRU 算法全称是最近最少使用算法(Least Recently Use),是一种简单的缓存策略。
通常用在对象池等需要频繁获取但是又需要释放不用的地方。
代码实现的基本原理就是使用链表,当某个元素被访问时(Get或Set)就将该元素放到链表的头部或者尾部(根据用户自己定义规则即可)当达到了缓存的最大容量时对最不常使用的元素进行移除(移除的时候可以定义一系列的规则,用于判读如何移除,是否移除)
下面直接贴出来代码供大家参考
csharp
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace ET
{
public class LruCache<TKey, TValue>: IEnumerable<KeyValuePair<TKey, TValue>>
{
private const int DEFAULT_CAPACITY = 255;
private int capacity;
private ReaderWriterLockSlim locker;
private Dictionary<TKey, TValue> dictionary;
private LinkedList<TKey> linkedList;
private Func<TKey, TValue, bool> checkCanPopFunc;
private Action<TKey, TValue> popCb;
public LruCache(): this(DEFAULT_CAPACITY)
{
}
public LruCache(int capacity)
{
this.locker = new ReaderWriterLockSlim();
this.capacity = capacity > 0? capacity : DEFAULT_CAPACITY;
this.dictionary = new Dictionary<TKey, TValue>(DEFAULT_CAPACITY);
this.linkedList = new LinkedList<TKey>();
}
/// <summary>
/// 设置检测什么条件可以释放
/// </summary>
/// <param name="func"></param>
public void SetCheckCanPopCallBack(Func<TKey, TValue, bool> func)
{
this.checkCanPopFunc = func;
}
/// <summary>
/// 设置如何释放
/// </summary>
/// <param name="action"></param>
public void SetPopCallBack(Action<TKey, TValue> action)
{
this.popCb = action;
}
public TValue this[TKey t]
{
get
{
if (TryGet(t, out TValue val))
return val;
throw new ArgumentException();
}
set
{
Set(t,value);
}
}
public bool TryGet(TKey key, out TValue value)
{
this.locker.EnterUpgradeableReadLock();
try
{
bool b = this.dictionary.TryGetValue(key, out value);
if (b)
{
this.locker.EnterWriteLock();
try
{
this.linkedList.Remove(key);
this.linkedList.AddFirst(key);
}
finally
{
this.locker.ExitWriteLock();
}
}
return b;
}
finally
{
this.locker.ExitUpgradeableReadLock();
}
}
public void Set(TKey key, TValue value)
{
this.locker.EnterWriteLock();
try
{
if (this.checkCanPopFunc != null)
this.MakeFreeSpace();
this.dictionary[key] = value;
this.linkedList.Remove(key);
this.linkedList.AddFirst(key);
//没有设置检测释放条件的话,容量超载了就移除
if (this.checkCanPopFunc == null && this.linkedList.Count > this.capacity)
{
this.dictionary.Remove(this.linkedList.Last.Value);
this.linkedList.RemoveLast();
}
}
finally
{
this.locker.ExitWriteLock();
}
}
public Dictionary<TKey, TValue> GetAll()
{
return this.dictionary;
}
public void Remove(TKey key)
{
this.locker.EnterWriteLock();
try
{
this.dictionary.Remove(key);
this.linkedList.Remove(key);
}
finally
{
this.locker.ExitWriteLock();
}
}
public bool TryOnlyGet(TKey key, out TValue value)
{
bool b = this.dictionary.TryGetValue(key, out value);
return b;
}
public bool ContainsKey(TKey key)
{
this.locker.EnterReadLock();
try
{
return this.dictionary.ContainsKey(key);
}
finally
{
this.locker.ExitReadLock();
}
}
public int Count
{
get
{
this.locker.EnterReadLock();
try
{
return this.dictionary.Count;
}
finally
{
this.locker.ExitReadLock();
}
}
}
public int Capacity
{
get
{
this.locker.EnterReadLock();
try
{
return this.capacity;
}
finally
{
this.locker.ExitReadLock();
}
}
set
{
this.locker.EnterUpgradeableReadLock();
try
{
if (value > 0 && this.capacity != value)
{
this.locker.EnterWriteLock();
try
{
this.capacity = value;
while (this.linkedList.Count > this.capacity)
{
this.linkedList.RemoveLast();
}
}
finally
{
this.locker.ExitWriteLock();
}
}
}
finally
{
this.locker.ExitUpgradeableReadLock();
}
}
}
public ICollection<TKey> Keys
{
get
{
this.locker.EnterReadLock();
try
{
return this.dictionary.Keys;
}
finally
{
this.locker.ExitReadLock();
}
}
}
public ICollection<TValue> Values
{
get
{
this.locker.EnterReadLock();
try
{
return this.dictionary.Values;
}
finally
{
this.locker.ExitReadLock();
}
}
}
public void Clear()
{
this.dictionary.Clear();
this.linkedList.Clear();
}
private void MakeFreeSpace()
{
LinkedListNode<TKey> node = this.linkedList.Last;
//检测最不常用的10个
int max_check_free_times = 10; //最大检测空闲次数
int cur_check_free_time = 0; //当前检测空闲次数
while (this.linkedList.Count + 1 > this.capacity)
{
if (node == null)
break;
LinkedListNode<TKey> tuple_prev = node.Previous;
if (this.checkCanPopFunc == null || this.checkCanPopFunc(node.Value, this.dictionary[node.Value]))
{
//可以释放
var value = this.dictionary[node.Value];
this.dictionary.Remove(node.Value);
this.linkedList.RemoveLast();
this.popCb?.Invoke(node.Value, value);
}
else
{
cur_check_free_time++;
if (cur_check_free_time >= max_check_free_times)
{
break;
}
}
node = tuple_prev;
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (var item in this.dictionary)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
foreach (var item in this.dictionary)
{
yield return item;
}
}
}
}