题
我正在使用一个 Dictionary<string, int>
哪里的 int
是密钥的计数。
现在,我需要访问字典中最后插入的键,但我不知道它的名称。明显的尝试:
int LastCount = mydict[mydict.keys[mydict.keys.Count]];
不起作用,因为 Dictionary.Keys
没有实现 []-索引器。
我只是想知道有没有类似的课程?我想过使用堆栈,但它只存储一个字符串。我现在可以创建自己的结构,然后使用 Stack<MyStruct>
, ,但我想知道是否还有另一种选择,本质上是一个在键上实现 [] 索引器的字典?
其他提示
您可以使用 有序字典.
表示可以通过密钥或索引访问的密钥/值对的集合。
字典是一个哈希表,所以你不知道插入的顺序!
如果您想知道最后插入的键,我建议扩展字典以包含 LastKeyInserted 值。
例如。:
public MyDictionary<K, T> : IDictionary<K, T>
{
private IDictionary<K, T> _InnerDictionary;
public K LastInsertedKey { get; set; }
public MyDictionary()
{
_InnerDictionary = new Dictionary<K, T>();
}
#region Implementation of IDictionary
public void Add(KeyValuePair<K, T> item)
{
_InnerDictionary.Add(item);
LastInsertedKey = item.Key;
}
public void Add(K key, T value)
{
_InnerDictionary.Add(key, value);
LastInsertedKey = key;
}
.... rest of IDictionary methods
#endregion
}
但是当你使用时你会遇到问题 .Remove()
因此,为了克服这个问题,您必须保留插入的键的有序列表。
为什么不扩展字典类来添加最后一个键插入的属性。也许像下面这样?
public class ExtendedDictionary : Dictionary<string, int>
{
private int lastKeyInserted = -1;
public int LastKeyInserted
{
get { return lastKeyInserted; }
set { lastKeyInserted = value; }
}
public void AddNew(string s, int i)
{
lastKeyInserted = i;
base.Add(s, i);
}
}
你总是可以这样做:
string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]
但我不会推荐它。无法保证最后插入的键将位于数组的末尾。键的排序 在 MSDN 上 未指定,并且可能会发生变化。在我非常简短的测试中,它似乎确实是按插入顺序排列的,但是您最好按照您的建议构建适当的簿记,例如堆栈(尽管我不认为需要基于您的结构)其他语句)——如果您只需要知道最新的密钥,则可以使用单变量缓存。
我认为您可以做这样的事情,语法可能是错误的,一段时间以来就没有使用C#来获取最后一项
Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();
或者使用 Max 而不是 Last 来获取最大值,我不知道哪一个更适合您的代码。
一种替代方案是 键控集合 如果键嵌入到值中。
只需在密封类中创建一个基本实现即可使用。
所以要更换 Dictionary<string, int>
(这不是一个很好的例子,因为 int 没有明确的键)。
private sealed class IntDictionary : KeyedCollection<string, int>
{
protected override string GetKeyForItem(int item)
{
// The example works better when the value contains the key. It falls down a bit for a dictionary of ints.
return item.ToString();
}
}
KeyedCollection<string, int> intCollection = new ClassThatContainsSealedImplementation.IntDictionary();
intCollection.Add(7);
int valueByIndex = intCollection[0];
我同意帕特里克回答的第二部分。即使在某些测试中它似乎保持插入顺序,文档(以及字典和哈希的正常行为)也明确指出顺序是未指定的。
你只是在自找麻烦,具体取决于按键的顺序。添加您自己的簿记(正如帕特里克所说,只是最后添加的键的单个变量)以确保确定。另外,不要被字典上的所有方法(例如 Last 和 Max)所诱惑,因为这些方法可能与键比较器相关(我对此不确定)。
如果您决定使用容易被破坏的危险代码,此扩展函数将从 Dictionary<K,V>
根据其内部索引(对于 Mono 和 .NET 目前看来与通过枚举得到的顺序相同) Keys
财产)。
最好使用 Linq: dict.Keys.ElementAt(i)
, ,但该函数将迭代 O(N);以下是 O(1) 但有反射性能损失。
using System;
using System.Collections.Generic;
using System.Reflection;
public static class Extensions
{
public static TKey KeyByIndex<TKey,TValue>(this Dictionary<TKey, TValue> dict, int idx)
{
Type type = typeof(Dictionary<TKey, TValue>);
FieldInfo info = type.GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance);
if (info != null)
{
// .NET
Object element = ((Array)info.GetValue(dict)).GetValue(idx);
return (TKey)element.GetType().GetField("key", BindingFlags.Public | BindingFlags.Instance).GetValue(element);
}
// Mono:
info = type.GetField("keySlots", BindingFlags.NonPublic | BindingFlags.Instance);
return (TKey)((Array)info.GetValue(dict)).GetValue(idx);
}
};
您提出问题的方式使我相信字典中的 int 包含该项目在字典中的“位置”。从密钥未按添加顺序存储的断言来看,如果这是正确的,则意味着keys.Count(或.Count - 1,如果您使用的是从零开始的)应该仍然总是最后输入的键的编号?
如果这是正确的,是否有任何原因不能改为使用 Dictionary<int, string> 以便可以使用 mydict[ mydict.Keys.Count ]?
我不知道这是否可行,因为我很确定键不是按添加顺序存储的,但您可以将 KeysCollection 转换为列表,然后获取列表中的最后一个键...但值得一看。
我唯一能想到的另一件事是将键存储在查找列表中,并在将键添加到字典之前将它们添加到列表中......但这并不漂亮。
要扩展 Daniels 的帖子和他对密钥的评论,由于密钥无论如何都嵌入在值中,您可以诉诸于使用 KeyValuePair<TKey, TValue>
作为值。这样做的主要原因是,一般来说,密钥不一定可以直接从值导出。
然后它看起来像这样:
public sealed class CustomDictionary<TKey, TValue>
: KeyedCollection<TKey, KeyValuePair<TKey, TValue>>
{
protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
{
return item.Key;
}
}
要像前面的示例一样使用它,您需要执行以下操作:
CustomDictionary<string, int> custDict = new CustomDictionary<string, int>();
custDict.Add(new KeyValuePair<string, int>("key", 7));
int valueByIndex = custDict[0].Value;
int valueByKey = custDict["key"].Value;
string keyByIndex = custDict[0].Key;
您还可以使用 SortedList 及其通用对应项。这两个类以及 Andrew Peters 的回答中提到的 OrderedDictionary 是字典类,其中可以通过索引(位置)以及键来访问项目。如何使用这些类你可以找到: 排序列表类 , SortedList 通用类 .
字典对于使用索引作为参考可能不是很直观,但是,您可以对数组进行类似的操作 键值对:
前任。KeyValuePair<string, string>[] filters;
视觉工作室的 用户之声 给出一个链接 通用 OrderedDictionary 实现 由多特莫尔。
但是,如果您只需要通过索引获取键/值对,而不需要通过键获取值,则可以使用一种简单的技巧。声明一些泛型类(我称之为 ListArray),如下所示:
class ListArray<T> : List<T[]> { }
您还可以使用构造函数声明它:
class ListArray<T> : List<T[]>
{
public ListArray() : base() { }
public ListArray(int capacity) : base(capacity) { }
}
例如,您从文件中读取一些键/值对,只想按读取顺序存储它们,以便稍后通过索引获取它们:
ListArray<string> settingsRead = new ListArray<string>();
using (var sr = new StreamReader(myFile))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] keyValueStrings = line.Split(separator);
for (int i = 0; i < keyValueStrings.Length; i++)
keyValueStrings[i] = keyValueStrings[i].Trim();
settingsRead.Add(keyValueStrings);
}
}
// Later you get your key/value strings simply by index
string[] myKeyValueStrings = settingsRead[index];
您可能已经注意到,ListArray 中不一定只有键/值对。项目数组可以是任意长度,就像锯齿状数组一样。