using System.Collections.Generic; namespace ZeroLevel.Services.Cache { /// /// A least-recently-used cache stored like a dictionary. /// /// The type of the key to the cached item. /// The type of the cached item. public sealed class LRUCache { /// /// Default maximum number of elements to cache. /// private const int DefaultCapacity = 255; private readonly object _lockObj = new object(); private readonly int _capacity; private readonly Dictionary _cacheMap; private readonly LinkedList _cacheList; /// /// Initializes a new instance of the class. /// public LRUCache() : this(DefaultCapacity) { } /// /// Initializes a new instance of the class. /// /// Maximum number of elements to cache. public LRUCache(int capacity) { _capacity = capacity > 0 ? capacity : DefaultCapacity; _cacheMap = new Dictionary(); _cacheList = new LinkedList(); } /// /// Gets the value associated with the specified key. /// /// The key of the value to get. /// When this method returns, contains the value associated with /// the specified key, if the key is found; otherwise, the default value for the /// type of the parameter. /// true if contains an element with the specified key; otherwise, false. public bool TryGet(TKey key, out TValue value) { lock (_lockObj) { if (_cacheMap.TryGetValue(key, out var entry)) { Touch(entry.Node); value = entry.Value; return true; } } value = default(TValue)!; return false; } /// /// Adds the specified key and value to the cache. /// /// The key of the element to add. /// The value of the element to add. public void Set(TKey key, TValue value) { lock (_lockObj) { if (!_cacheMap.TryGetValue(key, out var entry)) { LinkedListNode node; if (_cacheMap.Count >= _capacity) { node = _cacheList.Last; _cacheMap.Remove(node.Value); _cacheList.RemoveLast(); node.Value = key; } else { node = new LinkedListNode(key); } _cacheList.AddFirst(node); _cacheMap.Add(key, new Entry(node, value)); } else { entry.Value = value; _cacheMap[key] = entry; Touch(entry.Node); } } } private void Touch(LinkedListNode node) { if (node != _cacheList.First) { _cacheList.Remove(node); _cacheList.AddFirst(node); } } private struct Entry { public LinkedListNode Node; public TValue Value; public Entry(LinkedListNode node, TValue value) { this.Node = node; this.Value = value; } } } }