代码之家  ›  专栏  ›  技术社区  ›  Antonello

iDictionary是否有任何LRU实现?

  •  26
  • Antonello  · 技术社区  · 17 年前

    我想实现一个简单的内存中的LRU缓存系统,我正在考虑一个基于IDictionary实现的解决方案,它可以处理散列的LRU机制。 来自Java,我有经验 LinkedHashMap 这对我需要的东西很有用:我在任何地方都找不到类似的.NET解决方案。

    有人开发过它吗?有人有过这样的经历吗?

    9 回复  |  直到 8 年前
        1
  •  7
  •   Reed Copsey    17 年前

    基类库中没有执行此操作的库。

    在自由的一面,可能是类似于C5的东西 HashedLinkedList 会起作用。

    如果你愿意付钱,也许可以结账 this C# toolkit. 它包含一个实现。

        2
  •  41
  •   Rex    8 年前

    这是我们为自己的网站开发的一个非常简单、快速的实现。

    我们尽量改进代码,但要保证它的线程安全。 我认为代码非常简单和清晰,但是如果您需要一些解释或与如何使用它相关的指南,请毫不犹豫地问。

    namespace LRUCache
    {
        public class LRUCache<K,V>
        {
            private int capacity;
            private Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>> cacheMap = new Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>>();
            private LinkedList<LRUCacheItem<K, V>> lruList = new LinkedList<LRUCacheItem<K, V>>();
    
            public LRUCache(int capacity)
            {
                this.capacity = capacity;
            }
    
            [MethodImpl(MethodImplOptions.Synchronized)]
            public V get(K key)
            {
                LinkedListNode<LRUCacheItem<K, V>> node;
                if (cacheMap.TryGetValue(key, out node))
                {
                    V value = node.Value.value;
                    lruList.Remove(node);
                    lruList.AddLast(node);
                    return value;
                }
                return default(V);
            }
    
            [MethodImpl(MethodImplOptions.Synchronized)]
            public void add(K key, V val)
            {
                if (cacheMap.Count >= capacity)
                {
                    RemoveFirst();
                }
    
                LRUCacheItem<K, V> cacheItem = new LRUCacheItem<K, V>(key, val);
                LinkedListNode<LRUCacheItem<K, V>> node = new LinkedListNode<LRUCacheItem<K, V>>(cacheItem);
                lruList.AddLast(node);
                cacheMap.Add(key, node);
            }
    
            private void RemoveFirst()
            {
                // Remove from LRUPriority
                LinkedListNode<LRUCacheItem<K,V>> node = lruList.First;
                lruList.RemoveFirst();
    
                // Remove from cache
                cacheMap.Remove(node.Value.key);
            }
        }
    
        class LRUCacheItem<K,V>
        {
            public LRUCacheItem(K k, V v)
            {
                key = k;
                value = v;
            }
            public K key;
            public V value;
        }
    }
    
        3
  •  5
  •   Joel Purra    13 年前

    在谷歌搜索时找到你的答案,也找到了:

    http://code.google.com/p/csharp-lru-cache/

    csharp-lru-cache :lru缓存集合类库

    这是一个集合类, 作为最近使用的函数 隐藏物。它实现 ICollection<T> , 但也暴露了其他三个成员:

    • Capacity ,最大项目数 缓存可以包含。一旦 集合已满,添加 缓存中的新项将导致 最近使用过的物品 丢弃的。如果容量设置为0 在构建时,缓存将不会 自动放弃项目。
    • Oldest , 最老的(即最近使用最少的) 集合中的项。
    • DiscardingOldestItem ,引发了一个事件 当缓存将要放弃其 最古老的项目。这是一个非常 简单的实现。而其添加 移除方法是线程安全的,它 不能用在重物上 多线程环境是因为 整个集合在 这些方法。
        4
  •  4
  •   Chris Marisic    10 年前

    我最近发布了一个名为lurchtable的类来解决Linkedhashmap的C变体的需求。一个简短的讨论 LurchTable can be found here .

    基本特征:

    • 通过插入、修改或访问链接的并发字典
    • Dictionary/ConcurrentDictionary接口支持
    • Peek/TrydeQueue/DeQueue访问“最旧”条目
    • 允许对插入时强制执行的项进行硬限制
    • 显示用于添加、更新和删除的事件

    源代码: http://csharptest.net/browse/src/Library/Collections/LurchTable.cs

    github: https://github.com/csharptest/CSharpTest.Net.Collections

    HTML帮助: http://help.csharptest.net/

    pm>安装包 CSharpTest.Net.Collections

        5
  •  4
  •   Community Mohan Dere    9 年前

    这需要 Martin 的代码 Mr T 的建议,并使其成为StyleCop友好型。哦,它还允许在值从缓存中循环出来时对其进行处理。

    namespace LruCache
    {
        using System;
        using System.Collections.Generic;
    
        /// <summary>
        /// A least-recently-used cache stored like a dictionary.
        /// </summary>
        /// <typeparam name="TKey">
        /// The type of the key to the cached item
        /// </typeparam>
        /// <typeparam name="TValue">
        /// The type of the cached item.
        /// </typeparam>
        /// <remarks>
        /// Derived from https://stackoverflow.com/a/3719378/240845
        /// </remarks>
        public class LruCache<TKey, TValue>
        {
            private readonly Dictionary<TKey, LinkedListNode<LruCacheItem>> cacheMap =
                new Dictionary<TKey, LinkedListNode<LruCacheItem>>();
    
            private readonly LinkedList<LruCacheItem> lruList =
                new LinkedList<LruCacheItem>();
    
            private readonly Action<TValue> dispose;
    
            /// <summary>
            /// Initializes a new instance of the <see cref="LruCache{TKey, TValue}"/>
            /// class.
            /// </summary>
            /// <param name="capacity">
            /// Maximum number of elements to cache.
            /// </param>
            /// <param name="dispose">
            /// When elements cycle out of the cache, disposes them. May be null.
            /// </param>
            public LruCache(int capacity, Action<TValue> dispose = null)
            {
                this.Capacity = capacity;
                this.dispose = dispose;
            }
    
            /// <summary>
            /// Gets the capacity of the cache.
            /// </summary>
            public int Capacity { get; }
    
            /// <summary>Gets the value associated with the specified key.</summary>
            /// <param name="key">
            /// The key of the value to get.
            /// </param>
            /// <param name="value">
            /// 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 
            /// <paramref name="value" /> parameter. This parameter is passed
            /// uninitialized.
            /// </param>
            /// <returns>
            /// true if the <see cref="T:System.Collections.Generic.Dictionary`2" /> 
            /// contains an element with the specified key; otherwise, false.
            /// </returns>
            public bool TryGetValue(TKey key, out TValue value)
            {
                lock (this.cacheMap)
                {
                    LinkedListNode<LruCacheItem> node;
                    if (this.cacheMap.TryGetValue(key, out node))
                    {
                        value = node.Value.Value;
                        this.lruList.Remove(node);
                        this.lruList.AddLast(node);
                        return true;
                    }
    
                    value = default(TValue);
                    return false;
                }
            }
    
            /// <summary>
            /// Looks for a value for the matching <paramref name="key"/>. If not found, 
            /// calls <paramref name="valueGenerator"/> to retrieve the value and add it to
            /// the cache.
            /// </summary>
            /// <param name="key">
            /// The key of the value to look up.
            /// </param>
            /// <param name="valueGenerator">
            /// Generates a value if one isn't found.
            /// </param>
            /// <returns>
            /// The requested value.
            /// </returns>
            public TValue Get(TKey key, Func<TValue> valueGenerator)
            {
                lock (this.cacheMap)
                {
                    LinkedListNode<LruCacheItem> node;
                    TValue value;
                    if (this.cacheMap.TryGetValue(key, out node))
                    {
                        value = node.Value.Value;
                        this.lruList.Remove(node);
                        this.lruList.AddLast(node);
                    }
                    else
                    {
                        value = valueGenerator();
                        if (this.cacheMap.Count >= this.Capacity)
                        {
                            this.RemoveFirst();
                        }
    
                        LruCacheItem cacheItem = new LruCacheItem(key, value);
                        node = new LinkedListNode<LruCacheItem>(cacheItem);
                        this.lruList.AddLast(node);
                        this.cacheMap.Add(key, node);
                    }
    
                    return value;
                }
            }
    
            /// <summary>
            /// Adds the specified key and value to the dictionary.
            /// </summary>
            /// <param name="key">
            /// The key of the element to add.
            /// </param>
            /// <param name="value">
            /// The value of the element to add. The value can be null for reference types.
            /// </param>
            public void Add(TKey key, TValue value)
            {
                lock (this.cacheMap)
                {
                    if (this.cacheMap.Count >= this.Capacity)
                    {
                        this.RemoveFirst();
                    }
    
                    LruCacheItem cacheItem = new LruCacheItem(key, value);
                    LinkedListNode<LruCacheItem> node = 
                        new LinkedListNode<LruCacheItem>(cacheItem);
                    this.lruList.AddLast(node);
                    this.cacheMap.Add(key, node);
                }
            }
    
            private void RemoveFirst()
            {
                // Remove from LRUPriority
                LinkedListNode<LruCacheItem> node = this.lruList.First;
                this.lruList.RemoveFirst();
    
                // Remove from cache
                this.cacheMap.Remove(node.Value.Key);
    
                // dispose
                this.dispose?.Invoke(node.Value.Value);
            }
    
            private class LruCacheItem
            {
                public LruCacheItem(TKey k, TValue v)
                {
                    this.Key = k;
                    this.Value = v;
                }
    
                public TKey Key { get; }
    
                public TValue Value { get; }
            }
        }
    }
    
        6
  •  3
  •   JP Alioto    17 年前

    这个 Caching Application Block 的entlib有一个现成的lru清除选项,可以在内存中。你想要的东西可能有点重。

        7
  •  2
  •   Orion Edwards    17 年前

    我不相信。我当然见过在各种不相关的项目中多次实施手工轧制(这或多或少证实了这一点)。如果有,肯定至少有一个项目会使用它)。

    实现起来相当简单,通常通过创建一个包含 Dictionary 和A List .

    键按顺序进入列表,项按字典顺序进入。
    向集合中添加新项时,函数检查列表的长度,拉出最后一个键(如果太长),然后从字典中提取键和值进行匹配。其实没什么大不了的

        8
  •  2
  •   Jerry Ju    15 年前

    我喜欢劳伦斯的实践。哈希表+LinkedList是一个很好的解决方案。 关于线程,我不会锁定这个[MethodImpl(MethodImplOptions.Synchronized)],而是使用readerwriterlockslim或spin lock(因为争用通常很快)。 在get函数中,我首先检查它是否已经是第一个项,而不是总是删除和添加。这使您有可能保持在读卡器锁内,而不会阻塞其他读卡器。

        9
  •  0
  •   Tony Lee    17 年前

    如果它是一个ASP.NET应用程序,您可以使用缓存类[1],但您将与其他缓存的东西争夺空间,这可能是您想要的,也可能不是。

    〔1〕 http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx