【问题标题】:Accessing a Dictionary.Keys Key through a numeric index通过数字索引访问 Dictionary.Keys 键
【发布时间】:2010-09-05 11:26:06
【问题描述】:

我使用的是Dictionary<string, int>,其中int 是密钥的计数。

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

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

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

我只是想知道有没有类似的课程?我考虑过使用堆栈,但它只存储一个字符串。我现在可以创建自己的结构,然后使用Stack<MyStruct>,但我想知道是否还有另一种选择,本质上是在键上实现 []-indexer 的字典?

【问题讨论】:

  • 如果你把那个变量装箱会发生什么?

标签: c# .net dictionary


【解决方案1】:

你总是可以这样做的:

string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]

但我不会推荐它。不能保证最后插入的键将位于数组的末尾。 Keys on MSDN 的顺序未指定,可能会发生变化。在我非常简短的测试中,它似乎是按插入顺序排列的,但你最好像堆栈一样建立适当的簿记 - 正如你所建议的那样(尽管我认为不需要基于你的结构其他语句)-- 或单变量缓存,如果您只需要知道最新的密钥。

【讨论】:

    【解决方案2】:

    我不知道这是否可行,因为我很确定键没有按添加顺序存储,但您可以将 KeysCollection 转换为列表,然后获取列表中的最后一个键...但值得一看。

    我能想到的唯一另一件事是将键存储在查找列表中,然后在将它们添加到字典之前将它们添加到列表中......这并不漂亮。

    【讨论】:

    • @Juan: KeyCollection 上没有 .Last() 方法
    • 我没有测试代码,但该方法记录在 [MSDN][1] 也许它的另一个版本的框架? [1]:msdn.microsoft.com/en-us/library/bb908406.aspx
    • 晚了 2 年,但它可能对某人有所帮助...请参阅下面我对 Juan 帖子的回复。 Last() 是一个扩展方法。
    【解决方案3】:

    我想你可以这样做,语法可能是错误的,有一段时间没有使用 C# 获取最后一项

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

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

    【讨论】:

    • 我要补充一点,由于“Last()”是一种扩展方法,您需要 .NET Framework 3.5 并在 .cs 文件顶部添加“使用 System.Linq”。
    • 最后试试这个(显然使用 Dist 时:-) KeyValuePair last = oAuthPairs.Last(); if (kvp.Key != last.Key) { _oauth_ParamString = _oauth_ParamString + "&"; }
    【解决方案4】:

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

    您只是根据键的顺序自找麻烦。添加您自己的簿记(正如帕特里克所说,最后添加的键只有一个变量)以确保。另外,不要被字典中的 Last 和 Max 等所有方法所诱惑,因为这些方法可能与键比较器有关(我不确定)。

    【讨论】:

      【解决方案5】:

      您对问题的措辞使我相信 Dictionary 中的 int 包含该项目在 Dictionary 中的“位置”。从键未按添加顺序存储的断言来看,如果这是正确的,那将意味着 keys.Count (或 .Count - 1,如果您使用从零开始)应该仍然总是最后输入的键的编号?

      如果这是正确的,有什么理由你不能使用 Dictionary 来使用 mydict[ mydict.Keys.Count ]?

      【讨论】:

        【解决方案6】:

        你为什么不扩展字典类来添加最后一个键插入属性。可能类似于以下内容?

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

        【讨论】:

        • 您将 lastKeyInserted 设置为最后插入的值。要么您打算将其设置为插入的最后一个键,要么您需要更好的变量和属性名称。
        【解决方案7】:

        您可以使用OrderedDictionary

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

        【讨论】:

        • 呃,19次upvotes之后,没有人提到OrderedDictionary仍然不允许通过索引获取key?
        • 您可以使用 OrderedDictionary 访问具有整数索引的值,但不能使用 System.Collections.Generic.SortedDictionary 索引需要是 TKey 的地方
        • OrderedDictionary 名称与此集合函数相关,以按照添加元素的顺序维护元素。在某些情况下,订单与排序具有相同的含义,但不在此集合中。
        【解决方案8】:

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

        如果您想知道最后插入的键,我建议您扩展 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() 时你会遇到问题,所以要克服这个问题,你必须保持插入的键的有序列表。

        【讨论】:

          【解决方案9】:

          如果您决定使用易受破坏的危险代码,此扩展函数将根据其内部索引从 Dictionary&lt;K,V&gt; 中获取一个键(对于 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);
              }
          };
          

          【讨论】:

          • 嗯,编辑以改进答案赢得了反对票。我是否没有明确说明代码(显然)是丑陋的,应该相应地考虑?
          【解决方案10】:

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

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

          不应该依赖字典中键的顺序。如果您需要订购,您应该使用OrderedDictionary,正如answer 中所建议的那样。此页面上的其他答案也很有趣。

          【讨论】:

          • 似乎不适用于HashTable System.Collections.ICollection'不包含'ElementAt'的定义,并且没有扩展方法'ElementAt'接受'System.Collections.ICollection'类型的第一个参数' 可以找到
          • 您可以使用ElementAtOrDefault 版本来处理无异常版本。
          • 看到如此明显错误的答案被接受并获得如此多的支持,真是令人恐惧。这是错误的,因为Dictionary&lt;TKey,TValue&gt; documentation 声明“Dictionary&lt;TKey, TValue&gt;.KeyCollection 中的键顺序未指定。”订单未定义,你无法确定最后一个位置(mydict.Count -1)
          • 这很可怕......但对我有帮助,因为我正在寻找我怀疑的确认,即您不能指望订单!!!谢谢@Falanwe
          • 对于某些人来说,顺序是不相关的 - 只是你经历了所有的钥匙这一事实。
          【解决方案11】:

          如果键嵌入在值中,另一种选择是KeyedCollection

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

          所以要替换 Dictionary&lt;string, int&gt;(这不是一个很好的例子,因为 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];
          

          【讨论】:

          • 关于密钥上的 cmets,请参阅我对此的后续回答。
          【解决方案12】:

          要扩展 Daniels 的帖子和他关于密钥的 cmets,因为无论如何密钥都嵌入在值中,您可以使用 KeyValuePair&lt;TKey, TValue&gt; 作为值。这样做的主要原因是,一般来说,密钥不一定可以直接从值派生。

          然后它看起来像这样:

          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;
          

          【讨论】:

            【解决方案13】:

            您还可以使用 SortedList 及其通用对应项。这两个类以及在 Andrew Peters 的回答中提到的 OrderedDictionary 是字典类,其中项目可以通过索引(位置)以及键访问。如何使用这些类你可以找到:SortedList ClassSortedList Generic Class

            【讨论】:

              【解决方案14】:

              字典对于使用索引作为参考可能不是很直观,但是,您可以使用 KeyValuePair 数组进行类似的操作:

              例如。 KeyValuePair&lt;string, string&gt;[] filters;

              【讨论】:

                【解决方案15】:

                Visual Studio 的 UserVoice 通过 dotmore 提供了指向 generic OrderedDictionary implementation 的链接。

                但是如果您只需要通过索引获取键/值对而不需要通过键获取值,您可以使用一个简单的技巧。声明一些泛型类(我称之为 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 中不一定只有一对键/值。项目数组可以是任意长度,例如锯齿状数组。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2015-10-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-05-01
                  • 2013-11-08
                  • 1970-01-01
                  相关资源
                  最近更新 更多