C#按值对象排序列表

我正在尝试在C#中创建对象的“有序”缓存,其中的顺序取决于访问的次数。

我已经研究过Dictionary,SortedList和SortedDictionary,它们非常接近,但并不完全符合我的要求。

我想要一个包含所有以前缓存的项目的列表,这些项目可以使用getHits()方法来确定缓存项目的顺序。

然后,我可以按名称访问该缓存,并增加项目的查看次数。

简化示例(在Pseudo C#中 ):

 class Result { public int Hits = 0; public string Name = ""; public void IncreaseHits() { this.hits++; } public Result(String name) { this.name = name; } } class Program { public MagicSortableType MyCache; //what structure to use? public main() { MyCache.Add(new Result("My result 1")); MyCache.Add(new Result("My result 2")); MyCache.Add(new Result("My result 3")); MyCache['My result 2'].IncreaseHits(); MyCache['My result 2'].IncreaseHits(); MyCache['My result 3'].IncreaseHits(); MyCache.SortDesc(); //what is the real C# equivalent? foreach(Result result in MyCache) { Console.Write(result.Name + " - hits " + result.Hits); } } } 

输出:

 My result 2 - hits 2 My result 3 - hits 1 My result 1 - hits 0 

当我需要这样的东西时,我创造了我称之为MruDictionary 。 它由LinkedListDictionary> (其中T是对象的类型,对象键是类型string )。

访问是通过字典。 访问项目时,它将移动到列表的头部。 添加项目时,会将其添加到列表的开头。 如果列表大小超出设置的最大值,则会删除列表中的最后一个节点。

这非常有效。 这些物品的使用次数没有按顺序排列,而是按严格的MRU顺序排列。 这通常会将最常用的项目保留在缓存中,但如果长时间没有使用热门项目,则会刷新。 为了我的目的,这非常有效。

我写了一篇关于它的文章。 有关说明的完整资料,请访问http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=626 。

如果你真的需要它,它应该很容易添加命中数。

在你的伪代码的基础上,这似乎是有效的:

 var MyCache = new Dictionary { {"My result 1", new Result("My result 1")}, {"My result 2", new Result("My result 2")}, {"My result 3", new Result("My result 3")}, {"My result 4", new Result("My result 4")} }; MyCache["My result 2"].IncreaseHits(); MyCache["My result 2"].IncreaseHits(); MyCache["My result 3"].IncreaseHits(); foreach (var result in MyCache.OrderByDescending(x => x.Value.Hits)) { Console.WriteLine(result.Value.Name + " - hits " + result.Value.Hits); } 

我猜你需要这样的东西:

 SortedDictionary MyCache = new SortedDictionary(); string strKey = "NewResult"; if (MyCache.ContainsKey(strKey)) { MyCache[strKey] = MyCache[strKey] + 1; } else { MyCache.Add(strKey, 1); } 

SortedDictionary按键排序

SortedDictionary – MSDN

表示在键上排序的键/值对的集合。

您可以将字典提取到List> ,然后根据teh值对它们进行排序,如:

 List> list = MyCache.ToList(); foreach (var item in list.OrderByDescending(r=> r.Value)) { Console.WriteLine(item.Key+ " - hits " + item.Value); } 

所以你可以:

 class Program { public static SortedDictionary MyCache = new SortedDictionary(); static void Main(string[] args) { AddToDictionary("Result1"); AddToDictionary("Result1"); AddToDictionary("Result2"); AddToDictionary("Result2"); AddToDictionary("Result2"); AddToDictionary("Result3"); List> list = MyCache.ToList(); foreach (var item in list.OrderByDescending(r=> r.Value)) { Console.WriteLine(item.Key+ " - hits " + item.Value); } } public static void AddToDictionary(string strKey) { if (MyCache.ContainsKey(strKey)) { MyCache[strKey] = MyCache[strKey] + 1; } else { MyCache.Add(strKey, 1); } } } 

然后输出将是:

 Result2 - hits 3 Result1 - hits 2 Result3 - hits 1 

不管你是不是喜欢这样的东西。

你可以存储两组关系; 所有对象,通过键快速进行检索,以及按Hits存储排序的所有对象。 这具有加速访问的额外优势 – 您可以非常快速地获得ResultHits ,因此它是当前和下一个索引。

获取结果时,我们锁定访问权限以确保我们以primefaces方式更改其顺序,然后返回对象。 在写出点击次数时我们也会作弊; 我们知道最受欢迎的是什么,然后我们可以向后走过那个集合 – 甚至可以真正提取List的键,对它进行排序,然后迭代它。

 public class PopularityContest{ private Dictionary> PopularityContainer { get; set; } private Dictionary ResultContainer { get; set; } private int MaxPopularity = 0; public PopularityContest(){ PopularityContainer = new Dictionary>(); ResultContainer = new Dictionary(); } private Object _SyncLock = new Object(); public Result GetResult(string resultKey) { Result result = ResultContainer[resultKey]; lock(_SyncLock) { int currentHits = result.Hits; if(PopularityContainer.ContainsKey(currentHits) && PopularityContainer[currentHits].Contains(result)) { PopularityContainer[currentHits].Remove(result); } if(!PopularityContainer.ContainsKey(currentHits + 1)) { PopularityContainer.Add(currentHits + 1, new List()); } PopularityContainer[currentHits + 1].Add(Result); if((currentHits + 1) > MaxPopularity) { MaxPopularity = currentHits + 1;} } return result; } public void WritePopularity() { //Here could also extract the keys to a List, sort it, and walk that. //Note, as this is a read operation, dependent upon ordering, you would also consider locking here. for(int i = MaxPopularity; i >= 0; i--) { if(PopularityContainer.Contains(i) && PopularityContainer[i].Count > 0) { //NB the order of items at key[i] is the order in which they achieved their popularity foreach(Result result in PopularityContainer[i]) { Console.WriteLine(String.Format("{0} has had {1} hits", result.ToString(), i)); } } } } } 

下面的缓存公开了一个简单的Add / Get接口,用于添加和检索缓存中的项目,这显然可以改进。 它实现了IEnumerable,它通过缓存枚举所需的行为。 这里显然存在需要解决的线程问题。

 public class Cache: IEnumerable { //Dictionary to hold the values of the cache private Dictionary m_cacheStore = new Dictionary(); //Dictionary to hold the number of times each key has been accessed private Dictionary m_cacheAccessCount = new Dictionary(); public T Get(string cacheKey) { if (m_cacheStore.ContainsKey(cacheKey)) { //Increment access counter if (!m_cacheAccessCount.ContainsKey(cacheKey)) m_cacheAccessCount.Add(cacheKey, 0); m_cacheAccessCount[cacheKey] = m_cacheAccessCount[cacheKey] + 1; return m_cacheStore[cacheKey]; } throw new KeyNotFoundException(cacheKey); } public int GetHits(string cacheKey) { return m_cacheAccessCount.ContainsKey(cacheKey) ? m_cacheAccessCount[cacheKey] : 0; } public void Add(string cacheKey, T cacheValue) { if(m_cacheStore.ContainsKey(cacheKey)) throw new ArgumentException(string.Format("An element with the key {0} already exists in the cache", cacheKey)); m_cacheStore.Add(cacheKey, cacheValue); } #region Implementation of IEnumerable public IEnumerator GetEnumerator() { foreach (var source in m_cacheAccessCount.OrderBy(kvp => kvp.Value)) { yield return m_cacheStore[source.Key]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } 

“正确”的方法是在MyCache类中实现IComparable( http://msdn.microsoft.com/en-us/library/system.icomparable.aspx )接口。

这将公开一个名为CompareTo的方法,您必须在代码中编写该方法。

您只需创建该方法并在其中放置一些逻辑,说明此对象是否大于,小于或等于传入的对象。

然后通过说int result = MyCache1.ComparTo(MyCache2);在客户端代码中使用它int result = MyCache1.ComparTo(MyCache2);

结果将是-1 0或1,基于它是否大于小于或等于。

那这个呢:

 var MyCache = new SortedDictionary(); MyCache['My result 2'] = (MyCache['My result 2'] ?? 0) + 1; 

你想要这样的东西吗?

 public class Result { public int Hits = 0; public string Name = ""; public void IncreaseHits() { this.hits++; } public Result(String name) { this.name = name; } } class Program { public Dictionary MyCache; //what structure to use? public main() { MyCache.Add("My result 1", new Result("My result 1")); MyCache.Add("My result 2", new Result("My result 2")); MyCache.Add("My result 3", new Result("My result 3")); MyCache["My result 2"].IncreaseHits(); MyCache["My result 2"].IncreaseHits(); MyCache["My result 3"].IncreaseHits(); foreach(Result result in MyCache.Values.OrderByDesc(x => x.Hits)) { Console.Write(result.Name + " - hits " + result.Hits); } } } 

另外

 public class MyCacheClass { private Dictionary cache = new Dictionary(); public void IncreaseHits(string name) { Result cached; if (!cache.TryGetValue(name, out cached)) { cached = cache.Add(new Result(name)); } cached.IncreaseHits(); } public string Add(string name) { // Need to block duplicates.... cache.Add(name, new Result(name)); } public IEnumerable SortDesc { get { return cache.Values.OrderByDesc(x => x.Hits); } } } class Program { MyCacheClass MyCache = new MyCacheClass(); MyCache.Add("result1"); MyCache.IncreaseHits("My result 2"); MyCache.IncreaseHits("My result 2"); MyCache.IncreaseHits("My result 3"); foreach(Result result in MyCache.SorDesc) { Console.WriteLine(string.Format("{0} - hits {1}",result.Name,result.Hits); } } 

为什么不使用经典List并使用sort方法对其进行排序并编写自己的比较delagate?

 MyCache.Sort(delegate(Result a, Result b) { if (a.hits > b.hits) return -1; if (a.hits < b.hits) return 1; return 0; }); 

如果您需要通过密钥访问,您可以拥有2个结构。 一个用于快速访问,另一个用于保存排序数据

 Dictionary accessMap; List MyCache; accessMap["Object 1"] = obj1; MyCache.add(obj1); accessMap[Object 1].Increase(); //sort MyCache foreach(Result result in MyCache) { Console.Write(result.Name + " - hits " + result.Hits); }