我正在尝试在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<string, Result> 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
答案 0 :(得分:2)
基于您的伪代码,这似乎正在起作用:
var MyCache = new Dictionary<string, Result>
{
{"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);
}
答案 1 :(得分:2)
当我需要这样的东西时,我创造了我称之为MruDictionary
的东西。它由LinkedList<T>
和Dictionary<string, LinkedListNode<T>>
组成(其中T
是对象的类型,对象键的类型为string
)。
通过字典访问。访问项目时,它将移动到列表的头部。添加项目时,会将其添加到列表的开头。如果列表大小超出设置的最大值,则删除列表中的最后一个节点。
这非常有效。这些物品的使用次数没有按顺序排列,而是按严格的MRU顺序排列。这个通常保留了缓存中最常用的项目,但是如果有一段很长的时间没有使用热门项目,它就会被刷新。为了我的目的,这非常有效。
我写了一篇关于它的文章。完整的来源包含说明,请访问http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=626。
如果你确实需要,可以很容易地添加命中数。
答案 2 :(得分:1)
我想你需要这样的东西:
SortedDictionary<string,int> MyCache = new SortedDictionary<string, int>();
string strKey = "NewResult";
if (MyCache.ContainsKey(strKey))
{
MyCache[strKey] = MyCache[strKey] + 1;
}
else
{
MyCache.Add(strKey, 1);
}
但SortedDictionary
按键
表示按键排序的键/值对的集合。
您可以将字典提取到List<KeyValuePair<string,int>>
,然后根据以下值来对它们进行排序:
List<KeyValuePair<string, int>> list = MyCache.ToList();
foreach (var item in list.OrderByDescending(r=> r.Value))
{
Console.WriteLine(item.Key+ " - hits " + item.Value);
}
所以你可以:
class Program
{
public static SortedDictionary<string, int> MyCache = new SortedDictionary<string, int>();
static void Main(string[] args)
{
AddToDictionary("Result1");
AddToDictionary("Result1");
AddToDictionary("Result2");
AddToDictionary("Result2");
AddToDictionary("Result2");
AddToDictionary("Result3");
List<KeyValuePair<string, int>> 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
答案 3 :(得分:1)
不管你是不是喜欢这样的事情。
你可以存储两组关系;所有对象,按键快速进行检索,所有对象按Hits
存储排序。这具有加快访问速度的额外优势 - 您可以快速获得Result
,Hits
,因此它是当前和下一个索引。
获取结果时,我们锁定访问权限以确保我们以原子方式更改其顺序,然后返回对象。在写出点击次数时我们也会作弊;我们知道最受欢迎的是什么,然后我们可以向后走过那个集合 - 甚至可以将密钥提取到List<Int32>
,对其进行排序,然后迭代它。
public class PopularityContest{
private Dictionary<int, List<Result>> PopularityContainer { get; set; }
private Dictionary<String, Result> ResultContainer { get; set; }
private int MaxPopularity = 0;
public PopularityContest(){
PopularityContainer = new Dictionary<int, List<Result>>();
ResultContainer = new Dictionary<String, Result>();
}
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<Result>());
}
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<Int32>, 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));
}
}
}
}
}
答案 4 :(得分:1)
下面的缓存公开了一个简单的Add / Get接口,用于添加和检索缓存中的项目,显然可以对其进行改进。它实现了IEnumerable,它通过缓存枚举所需的行为。这里显然存在需要解决的线程问题。
public class Cache<T>: IEnumerable<T>
{
//Dictionary to hold the values of the cache
private Dictionary<string, T> m_cacheStore = new Dictionary<string, T>();
//Dictionary to hold the number of times each key has been accessed
private Dictionary<string, int> m_cacheAccessCount = new Dictionary<string, int>();
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<T> GetEnumerator()
{
foreach (var source in m_cacheAccessCount.OrderBy(kvp => kvp.Value))
{
yield return m_cacheStore[source.Key];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
答案 5 :(得分:0)
“正确”的方法是在MyCache类中实现IComparable(http://msdn.microsoft.com/en-us/library/system.icomparable.aspx)接口。
这将公开一个名为CompareTo的方法,您必须在代码中编写它。
您只需创建该方法并在其中放置一些逻辑,说明此对象是否大于,小于或等于传入的对象。
然后,您可以通过int result = MyCache1.ComparTo(MyCache2);
结果将是-1 0或1,基于它是否大于小于或等于。
答案 6 :(得分:0)
这个怎么样:
var MyCache = new SortedDictionary<string, int?>();
MyCache['My result 2'] = (MyCache['My result 2'] ?? 0) + 1;
答案 7 :(得分:0)
你想要这样的东西。
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<string, Result> 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<string,Result> cache = new Dictionary<string, Result>();
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<Result> 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);
}
}
答案 8 :(得分:-2)
为什么不使用经典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<String, Result> accessMap;
List<Result> 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);
}