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

时间:2008-08-07 00:51:22

标签: c# .net dictionary

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

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

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

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

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

15 个答案:

答案 0 :(得分:214)

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

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

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

答案 1 :(得分:56)

您可以使用OrderedDictionary

  

表示键/值的集合   密钥可访问的对   或指数。

答案 2 :(得分:17)

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

如果你想知道最后插入的密钥,我建议扩展Dictionary以包含LastKeyInserted值。

E.g:

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()时,你会遇到问题,所以要克服这个问题,你必须保留一个有序的键列表。

答案 3 :(得分:8)

为什么不直接扩展字典类以添加最后一个键插入属性。可能会出现以下情况?

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

答案 4 :(得分:6)

您可以随时执行此操作:

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

但我不推荐它。无法保证最后插入的密钥位于数组的末尾。密钥on MSDN的排序未指定,可能会发生变化。在我非常简短的测试中,它看起来似乎是按照插入的顺序,但你最好建立像堆栈一样的正确簿记 - 正如你的建议(尽管我没有看到基于你的结构的需要)其他语句) - 如果您只需要知道最新的密钥,则使用单变量缓存。

答案 5 :(得分:5)

我认为你可以做这样的事情,语法可能是错误的,暂时没有使用过C# 获取最后一项

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

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

答案 6 :(得分:4)

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

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

答案 7 :(得分:4)

如果您决定使用可能破损的危险代码,此扩展函数将根据其内部索引从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);
    }
};

答案 8 :(得分:4)

如果密钥嵌入在值中,则一个替代方案是KeyedCollection

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

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

答案 9 :(得分:3)

你提出这个问题的方式让我相信字典中的int包含了字典中项目的“位置”。判断密钥没有按照添加的顺序存储,如果这是正确的,那就意味着keys.Count(或.Count - 1,如果你使用从零开始)应该仍然始终是最后输入的密钥的编号?

如果这是正确的,是否有任何理由你不能使用Dictionary&lt; int,string&gt;这样你就可以使用mydict [mydict.Keys.Count]了?

答案 10 :(得分:2)

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

我能想到的另一件事就是将密钥存储在查找列表中,然后在将它们添加到字典之前将密钥添加到列表中......这不是很好。

答案 11 :(得分:2)

要扩展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;

答案 12 :(得分:2)

您也可以使用SortedList及其Generic对应物。这两个类和安德鲁·彼得斯回答提到的OrderedDictionary是字典类,其中的项目可以通过索引(位置)和键来访问。如何使用这些课程:SortedList ClassSortedList Generic Class

答案 13 :(得分:2)

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

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

答案 14 :(得分:1)

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中的键/值对。项目数组可以是任何长度,如锯齿状数组。