排序字典按C#(LRU缓存)中的值排序

时间:2012-07-26 11:54:50

标签: c# algorithm caching collections

我想实现 LRU cache ,其中最近最少使用的元素将被异步驱逐。我目前的想法是使用Dictionary来存储<key,value>对,并跟踪对象的访问次数,以保持SortedDictionary <key, timestamp>。我们的想法是让异步线程从SortedDictionary获取LRU项并从缓存中删除。但为了实现这一点,SortedDictionary需要按值排序,而不是。

我本来可以使用单独的SortedList而不是SortedDictionary来保持{key和timestamp}按时间戳排序,但之后我将不得不进行“线性”查找以查找列表中的键(当我必须更新时间戳时,再次访问相同的键时) - 如果可能的话,我正在寻找比线性方式更好的方法。有人可以分享想法来处理这个问题吗?

所以,我的问题归结为:

我要在&lt; = logn time中查找密钥以更新时间戳,同时能够根据时间戳对密钥进行排序。

一种方法是保留SortedDictionary <{key,timestamp},null> hashcode(),根据{key,timestamp}的时间戳部分对键进行排序。虽然这很好,但问题是equals()只需要返回key.hashcode()(用于在更新时间戳时查找),而equals()也应该使用时间戳。因此,hashcode()和{{1}}存在冲突,所以觉得这不是一个好主意......

4 个答案:

答案 0 :(得分:4)

你应该做的是保留两个词典,一个按时间排序,一个按键排序。

请记住,字典只保存对实际对象的引用,因此用于更新对象的字典无关紧要。

要更新对象,请创建一个将更新字典

的函数
var oldObj = keyedObject[key];
timedObjects.Remove(oldObj.LastUpdateTime);
timedObjects.Add(myUpdatedObject.LastUpdateTime,myUpdatedObject);
keyedObject[key] = myUpdatedObject;

现在你可以通过时间和关键点跟踪同一个对象。

我只保留timedObjects中对象的一个​​引用。这有助于删除。

您可以根据需要继续修剪timedObjects字典。

Ofcource,在修剪时你必须记住,还有另一个字典keyedObject引用了同一个对象。仅仅呼叫Remove是不够的。

您的删除代码必须如下:

removeObject = timedObjects[timeToRemove];
timedObjects.Remove(timeToRemove);
keyedObject.Remove(removeObject.key);

timeToRemove主要来自for循环,您可以在其中决定要删除的对象

答案 1 :(得分:0)

您正在寻找的地图类型(至少在Java中)称为LinkedHashMap

来自javadoc:

  

使用Hash表和Map接口的链表实现   可预测的迭代顺序。此实现与HashMap不同   因为它维护着一个贯穿所有链接的双向链表   条目。该链表定义了迭代排序,即   通常是键插入地图的顺序   (插入顺序)。

     

提供了一个特殊的构造函数来创建一个链接的哈希映射   迭代顺序是其条目的最后顺序   访问,从最近访问到最近访问   (存取顺序)。这种地图非常适合构建LRU   高速缓存

Source for LinkedHashMap from the OpenJDK

AFAIK,C#中没有LinkedHashMap的现有实现。话虽如此,写一个也不是非常困难。

答案 2 :(得分:0)

不是sorteddictionary,而是编写自己的链表,并让Dictionary指向其节点作为值。它总是按时间戳排序,更新时间戳并删除最少使用的元素将为O(1)。

答案 3 :(得分:0)

这是c#中LRU缓存的实现。有效的O(1),但不是线程安全的;

    static void Main(string[] args)
    {
        var cache = new LruCache(3);
        cache.Put(1, 1);
        cache.Put(2, 2);
        Console.WriteLine(cache.Get(1));       // returns 1
        cache.Put(3, 3);    // evicts key 2
        Console.WriteLine(cache.Get(2));       // returns -1 (not found)
        cache.Put(4, 4);    // evicts key 1
        Console.WriteLine(cache.Get(1));       // returns -1 (not found)
        Console.WriteLine(cache.Get(3));       // returns 3
        Console.WriteLine(cache.Get(4));       // returns 4     

    }

    public class DoubleLinkedList
    {
        public int key;
        public int value;
        public DoubleLinkedList next;
        public DoubleLinkedList prev;
        public DoubleLinkedList(int k, int v)
        {
            key = k;
            value = v;
        }
    }

    public class LruCache
    {
        private int size;
        private int capacity;
        private Dictionary<int, DoubleLinkedList> map;
        private DoubleLinkedList head;
        private DoubleLinkedList tail;
        public LruCache(int cap)
        {
            capacity = cap;
            map = new Dictionary<int, DoubleLinkedList>();
            head = new DoubleLinkedList(0, 0);
            tail = new DoubleLinkedList(0, 0);
            head.next = tail;
            tail.prev = head;
        }

        public int Get(int key)
        {
            if (map.ContainsKey(key))
            {
                if (tail.prev.key != key)
                {
                    var node = map[key];
                    RemoveNode(node);
                    AddToEnd(node);
                }
                return map[key].value;
            }
            return -1;
        }

        private void AddToEnd(DoubleLinkedList node)
        {
            var beforeTail = tail.prev;
            node.prev = beforeTail;
            beforeTail.next = node;
            tail.prev = node;
            node.next = tail;
        }

        private void RemoveNode(DoubleLinkedList node)
        {
            var before = node.prev;
            before.next = node.next;
            node.next.prev = before;
        }

        public void Put(int key, int value)
        {
            if (map.ContainsKey(key))
            {
                map[key].value = value;
                var node = map[key];
                RemoveNode(node);
                AddToEnd(node);
            }
            else
            {
                size++;
                if (size > capacity)
                {
                    var node = head.next;
                    RemoveNode(node);
                    map.Remove(node.key);
                    size--;
                }

                var newNode = new DoubleLinkedList(key, value);
                AddToEnd(newNode);
                map.Add(key, newNode);
            }
        }
    }