C# ivate类软件 { 公钥{get;set;} 公共TValue值{get;set;} 公共长使用计数{get;set;} 公共LfuItem Prev{get;set;} 公共LfuItem Next{get;set;} } }

C# ivate类软件 { 公钥{get;set;} 公共TValue值{get;set;} 公共长使用计数{get;set;} 公共LfuItem Prev{get;set;} 公共LfuItem Next{get;set;} } },c#,iequalitycomparer,icomparer,memorycache,C#,Iequalitycomparer,Icomparer,Memorycache,在我看来,Add和Touch在O(1)中,不是吗 目前我没有看到任何关于\u first的用例,但可能其他人需要它。删除一项\u last就足够了 编辑 如果不需要向下移动操作,也可以使用单个链接列表。 编辑否单个链接列表将不起作用,因为MoveUp需要下一个指针来更改它的上一个指针。而不是在构造函数中使用IEqualityComparer和IComparer,您可以尝试使用IComparer和定义GetHashCode()的lambda。然后基于if(IComparer==0)和GetHash

在我看来,
Add
Touch
在O(1)中,不是吗

目前我没有看到任何关于
\u first
的用例,但可能其他人需要它。删除一项
\u last
就足够了

编辑 如果不需要向下移动操作,也可以使用单个链接列表。
编辑否单个链接列表将不起作用,因为MoveUp需要
下一个
指针来更改它的
上一个
指针。

而不是在构造函数中使用
IEqualityComparer
IComparer
,您可以尝试使用
IComparer
和定义
GetHashCode()
的lambda。然后基于
if(IComparer==0)
GetHashCode()=lambda


虽然我会说它很小,但是当
IComparer
返回0时,仍然存在哈希代码不匹配的风险。如果您想让代码的用户非常清楚,您可以通过在构造函数中使用两个lambda来扩展策略:
Func
用于
IComparer
IEqualityComparer
,和
Func
对于
GetHashCode

一种可能性是使用通用约束定义一个静态工厂方法,该方法采用一个比较器参数,该参数同时实现
IEqualityComparer
IComparer
。那么至少你不必把同一个对象传递给两个不同的参数。这听起来很有趣,但不知怎么的,我无法理解代码应该是什么样子。你能分享几行粗略的代码吗?;-)当然请看我的答案。我认为有必要检查一下自己是否可以实现
SortedSet
。如果递增
UseCount
,则此集合中只能向上更改一个位置。也许有一个O(1)的触摸实现。Insert应该是O(1),因为它被添加到列表的末尾(可能SortedSet会这样做)。我将尝试一个新的示例实现。有一个似乎很有趣——如果您将上次访问时间添加为密钥,则无法使用字典查找密钥,因为您不知道上次访问密钥的时间,因此无法计算密钥以查找字典。也许我没有正确理解你的解决方案(英语不是我的母语-对不起)。@Verarind我不是建议OP改变他的解决方案。显然,他已经有了一个可行的解决方案,其中他有一个并排的字典,一个排序集,使用字典进行查找,并使用排序集确定要删除哪些项。sortedset中的项有一个键作为属性,如果您是这样问的话,可以使用它在字典中查找该项。非常感谢。是的,也许我会再次评估LRU和LFU的优势。LRU仅使用字典和双链表就更容易实现,并且具有更多的O(1)属性。然而,我不知道这一变化将如何影响缓存命中率。“我需要做更多的工作。”我明白了。他有一个解决方案需要一个
i比较程序
。您告诉他更改它并为排序集使用时间戳。是的,你没有要求他完全改变它。他的解决方案也需要O(logn)来接触元素。为什么不将其完全更改为在任何情况下都能在O(1)中工作并且不需要任何UseCount的解决方案?他还说,不同的设计是受欢迎的。@Verarind不,我告诉他所要做的就是不要用实际对象的比较作为应该放弃的项目的平局,他应该让平局发生。如果有了这种改变,他的解决方案是好的,那么就很好了;这里没有别的事可做。如果他的整个解决方案,除了这个问题之外,也太慢了,那么这是一个完全不同的问题,与被问的问题完全不同。他似乎觉得代码的其余部分工作正常,所以如果是的话,我认为没有理由重新编写整个代码。只是因为在这个特定的例子中,比较器碰巧实现了
IEqualityComparer
IComparer
,并不意味着情况总是这样。对于所有其他情况,他需要创建一个新类来包装他拥有的两个比较器,然后该类需要确保两个比较器共享一个标识。或多或少,这只是将问题推到别处,而不是删除它。@Servy OP要求提供与我的评论相关的代码,但评论时间太长了一点。我同意,这只是让OP的工作变得简单而已。对于这个用例,我可能会给构造函数留下两个比较器。一般来说,对于
IEqualityComparer
,甚至不能保证
GetHashCode
Equals
的实现是一致的,更不用说与一些
IComparer
保持一致了。这就是为什么我们有代码审查和自动测试。看起来很好,但是我看不到强制执行缓存大小限制的代码(这是事情开始变得棘手的地方)。好的观点。我更新了
Add
并添加了
RemoveLast
。这应该说明它是如何工作的。嗯,看看
trymovup()
,我想一定有一个循环。例如,如果我们有使用计数为3、2、2、2、2、1的条目,并且触摸最右边的“2”,使其使用计数变为3,则它应该向左移动4次。否则,列表顶部将不会有最常用的项。所以这是我们唯一有O(n)的地方。是的。几分钟前我意识到了这一点。我的假设有误。可能会有超过1次的移动!如果所有
n
条目具有相同的
UseCount
最后一个i
public class LFUCache<TKey,TValue>
{
    private readonly Dictionary<TKey,CacheItem> entries;
    private readonly SortedSet<CacheItem> lfuList;

    private class CacheItem
    {
        public TKey Key;
        public TValue Value;
        public int UseCount;
    }

    private class CacheItemComparer : IComparer<CacheItem>
    {
        private readonly IComparer<TKey> cacheKeyComparer;

        public CacheItemComparer(IComparer<TKey> cacheKeyComparer)
        {
            this.cacheKeyComparer = cacheKeyComparer;
            if (cacheKeyComparer == null)
                this.cacheKeyComparer = Comparer<TKey>.Default;
        }

        public int Compare(CacheItem x, CacheItem y)
        {
            int UseCount = x.UseCount - y.UseCount;
            if (UseCount != 0) return UseCount;
            return cacheKeyComparer.Compare(x.Key, y.Key);
        }
    }

    public LFUCache(int capacity, IEqualityComparer<TKey> keyEqualityComparer,
                  IComparer<TKey> keyComparer)  // <- here's my problem
    {
        // ...
        entries = new Dictionary<TKey, CacheItem>(keyEqualityComparer);
        lfuList = new SortedSet<CacheItem>(new CacheItemComparer(keyComparer));
    }
    // ...
}
var cache = new LFUCache<string, int>(10000,
    StringComparer.InvariantCultureIgnoreCase,
    StringComparer.InvariantCultureIgnoreCase);
public class LFUCache<TKey,TValue>
{
    public static LFUCache<TKey, TValue> Create<TComp>(int capacity, TComp comparer) where TComp : IEqualityComparer<TKey>, IComparer<TKey>
    {
        return new LFUCache<TKey, TValue>(capacity, comparer, comparer);
    }
}
var cache = LFUCache<string, int>.Create(10000, StringComparer.InvariantCultureIgnoreCase);
public class LfuCache<TKey, TValue>
{
    private readonly Dictionary<TKey, LfuItem> _items;

    private readonly int _limit;

    private LfuItem _first, _last;

    public LfuCache(int limit, IEqualityComparer<TKey> keyComparer = null)
    {
        this._limit = limit;
        this._items = new Dictionary<TKey,LfuItem>(keyComparer);
    }

    public void Add(TKey key, TValue value)
    {
        if (this._items.Count == this._limit)
        {
            this.RemoveLast();
        }

        var lfuItem = new LfuItem { Key = key, Value = value, Prev = this._last };
        this._items.Add(key, lfuItem);

        if (this._last != null)
        {
            this._last.Next = lfuItem;
            lfuItem.Prev = this._last;
        }

        this._last = lfuItem;

        if (this._first == null)
        {
            this._first = lfuItem;
        }
    }

    public TValue this[TKey key]
    {
        get
        {
            var lfuItem = this._items[key];
            ++lfuItem.UseCount;

            this.TryMoveUp(lfuItem);

            return lfuItem.Value;
        }
    }

    private void TryMoveUp(LfuItem lfuItem)
    {
        if (lfuItem.Prev == null || lfuItem.Prev.UseCount >= lfuItem.UseCount) // maybe > if you want LRU and LFU
        {
            return;
        }

        var prev = lfuItem.Prev;
        prev.Next = lfuItem.Next;
        lfuItem.Prev = prev.Prev;
        prev.Prev = lfuItem;

        if (lfuItem.Prev == null)
        {
            this._first = lfuItem;
        }
    }

    private void RemoveLast()
    {
        if (this._items.Remove(this._last.Key))
        {
            this._last = this._last.Prev;
            if (this._last != null)
            {
                this._last.Next = null;
            }
        }
    }

    private class LfuItem
    {
        public TKey Key { get; set; }

        public TValue Value { get; set; }

        public long UseCount { get; set; }

        public LfuItem Prev { get; set; }

        public LfuItem Next { get; set; }
    }
}