通过数字索引访问Dictionary.Keys键

我正在使用Dictionary<string, int> ,其中int是键的计数。

现在,我需要访问Dictionary中最后插入的Key,但是我不知道它的名字。 显而易见的尝试:

 int LastCount = mydict[mydict.keys[mydict.keys.Count]]; 

不起作用,因为Dictionary.Keys不实现[] -indexer。

我只是想知道有没有类似的课程? 我想过使用堆栈,但只存储一个string。 我现在可以创build自己的结构,然后使用Stack<MyStruct> ,但是我想知道是否有另一种替代scheme,本质上是一个在Keys上实现[] -indexer的Dictionary?

正如@Falanwe在评论中指出的那样,做这样的事情是不正确的

 int LastCount = mydict.Keys.ElementAt(mydict.Count -1); 

不应该依赖于字典中的键的顺序。 如果你需要sorting,你应该使用OrderedDictionary ,正如在这个答案中所build议的那样。 在这个页面上的其他答案也很有趣。

你可以使用OrderedDictionary 。

表示键或索引可访问的键/值对的集合。

字典是一个哈希表,所以你不知道插入的顺序!

如果你想知道最后插入的键,我会build议扩展Dictionary以包含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上Keys的sorting是未指定的,并且可能会更改。 在我非常简短的testing中,它似乎是按照插入的顺序,但是你最好是像堆栈一样build立适当的簿记 – 正如你所build议的(尽pipe我没有看到基于你的结构的需要其他语句) – 或单个variablescaching,如果你只需要知道最新的密钥。

我认为你可以做这样的事情,语法可能是错误的,没有使用C#在一段时间得到最后一个项目

 Dictionary<string, int>.KeyCollection keys = mydict.keys; string lastKey = keys.Last(); 

或者使用Max而不是Last来获得最大值,我不知道哪一个更适合你的代码。

如果您决定使用破坏性的危险代码,则此扩展函数将根据其内部索引从Dictionary获取一个键(Mono和.NET目前的排列顺序与枚举Keys属性)。

最好使用Linq:dict.Keys.ElementAt(i),但我不知道这个函数是否足够聪明,不能迭代O(N)。 以下是O(1)但是reflectionperformance惩罚。

 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); } }; 

如果密钥embedded到值中,则可以select一个KeyedCollection 。

只需在密封的类中创build一个基本的实现即可使用。

所以要replaceDictionary<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]; 

我同意帕特里克答案的第二部分。 即使在某些testing中似乎仍然保持插入顺序,但文档(以及字典和散列的正常行为)明确指出sorting是未指定的。

根据按键的顺序,您只是要求麻烦。 添加自己的簿记(正如帕特里克说的,只是最后一个添加键的单个variables)可以肯定。 此外,不要被所有的方法,如字典上的最后和最大的诱惑,因为那些可能是关键的比较(我不知道这一点)。

你提到这个问题的方式让我相信,词典中的int包含了该词典中的项目“位置”。 从断言来看,键不是按照它们添加的顺序存储的,如果这是正确的,那将意味着keys.Count(或者.Count – 1,如果你使用的是从零开始的)仍然应该始终是最后input的密钥的数量?

如果这是正确的,是否有任何理由,你不能改为使用Dictionary <int,string>,以便您可以使用mydict [mydict.Keys.Count]?

我不知道这是否会工作,因为我敢肯定,密钥没有存储在他们添加的顺序,但您可以将KeysCollection强制转换为列表,然后获取列表中的最后一个键…但值得一看。

我唯一能想到的另一件事是将键存储在查找列表中,并将键添加到列表中,然后再将它们添加到字典中……这不是很好。

为了扩展Daniels的post及其关于密钥的评论,由于密钥embedded在值中,所以可以使用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是字典类,其中的项目可以通过索引(位置)以及键来访问。 如何使用这些类,你可以find: SortedList Class , SortedList Generic Class 。

字典可能不是非常直观的使用索引来引用,但是,您可以使用KeyValuePair数组进行类似的操作:

恩。 KeyValuePair<string, string>[] filters;

Visual Studio的UserVoice提供了dotmore的genericsOrderedDictionary实现的链接。

但是,如果您只需要按索引获取键/值对,并且不需要通过键获取值,则可以使用一个简单的技巧。 声明一些generics类(我称之为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中的键/值对。 项目数组可以是任何长度,如锯齿arrays。