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

Accessing a Dictionary.Keys Key through a numeric index

提问人:Michael Stum 提问时间:8/7/2008 最后编辑:user2771704Michael Stum 更新时间:1/3/2020 访问量:279469

问:

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

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

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

不起作用,因为不实现 [] 索引器。Dictionary.Keys

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

C# .NET 字典

评论

1赞 Paul Prewett 7/30/2014
如果将该变量装箱会发生什么?

答:

6赞 Patrick 8/7/2008 #1

你总是可以这样做:

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

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

2赞 lomaxx 8/7/2008 #2

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

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

评论

0赞 Juan 8/7/2008
我没有测试代码,但该方法记录在 [MSDN][1] 上,也许是框架的另一个版本?[1]:msdn.microsoft.com/en-us/library/bb908406.aspx
0赞 lomaxx 8/7/2008
@Juan:没有.KeyCollection 上的 Last() 方法
0赞 SuperOli 11/5/2010
晚了 2 年,但它可能会帮助某人......请参阅下面我对 Juan 帖子的回复。Last() 是一个扩展方法。
5赞 Juan 8/7/2008 #3

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

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

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

评论

2赞 SuperOli 11/5/2010
我要补充一点,由于“Last()”是一个扩展方法,因此您需要 .NET Framework 3.5 并在 .cs 文件的顶部添加“使用 System.Linq”。
0赞 Tim Windsor 6/25/2013
最后尝试这个(当使用 Dist<string,string> 时,显然 :-)KeyValuePair<字符串,字符串> last = oAuthPairs.Last();如果 (kvp.键 != last。Key) { _oauth_ParamString = _oauth_ParamString + “&”; }
4赞 Stephen Pellicer 8/7/2008 #4

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

你只是在自找麻烦,这取决于钥匙的顺序。添加您自己的簿记(正如 Patrick 所说,只是最后一个添加的键的单个变量)以确保。另外,不要被字典上的 Last 和 Max 等所有方法所诱惑,因为这些方法可能与键比较器有关(我不确定)。

3赞 Jeremy Privett 8/7/2008 #5

你提出问题的方式让我相信字典中的 int 包含项目在字典中的“位置”。从密钥未按添加顺序存储的断言来看,如果这是正确的,则意味着密钥。计数(或 .计数 - 1,如果您使用的是从零开始的)仍应始终是最后输入的键的编号?

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

8赞 Calanus 8/7/2008 #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);
    }
}

评论

2赞 Fantius 3/2/2011
将 lastKeyInserted 设置为最后插入的值。要么你打算将其设置为最后一个插入的键,要么你需要为变量和属性提供更好的名称。
59赞 Andrew Peters 8/8/2008 #7

可以使用 OrderedDictionary

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

评论

45赞 Lazlo 7/14/2011
呃,在19次点赞之后,没有人提到OrderedDictionary仍然不允许按索引获取密钥?
1赞 Maxence 10/5/2012
可以使用 OrderedDictionary 访问具有整数索引的值,但不能使用 System.Collections.Generic.SortedDictionary<TKey, TValue 访问值>其中索引必须是 TKey
0赞 Sharunas Bielskis 1/4/2020
OrderedDictionary 名称与此集合函数相关,以维护元素的添加顺序。在某些情况下,顺序与排序具有相同的含义,但在此集合中不然。
18赞 Ben 4/16/2009 #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()

4赞 Glenn Slayden 4/12/2010 #9

如果您决定使用容易损坏的危险代码,此扩展函数将根据其内部索引从 a 获取密钥(对于 Mono 和 .NET 来说,当前似乎与通过枚举属性获得的顺序相同)。Dictionary<K,V>Keys

最好使用 Linq: ,但该函数将迭代 O(N);以下是 O(1),但反射性能会受到惩罚。dict.Keys.ElementAt(i)

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

评论

0赞 Glenn Slayden 1/3/2018
嗯,编辑以改进答案赢得了反对票。难道我没有明确表示该代码(显然)很丑陋,应该相应地考虑吗?
232赞 Vitor Hugo 1/19/2011 #10

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

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

不应依赖于字典中键的顺序。如果需要排序,则应使用 OrderedDictionary,如本答案中所示。此页面上的其他答案也很有趣。

评论

1赞 v.oddou 2/20/2015
似乎不适用于 System.Collections.ICollection' 不包含“ElementAt”的定义,并且找不到接受类型为“System.Collections.ICollection”的第一个参数的扩展方法“ElementAt”HashTable
0赞 Tarık Özgün Güner 2/26/2015
您可以使用版本来处理无例外版本。ElementAtOrDefault
23赞 Falanwe 6/11/2016
看到这样一个公然错误的答案被接受并投了那么多票,真是太可怕了。这是错误的,因为正如 Dictionary<TKey,TValue> 文档所述“未指定 中的键的顺序”。顺序未定义,您无法确定哪个在最后一个位置(Dictionary<TKey, TValue>.KeyCollectionmydict.Count -1)
1赞 Charlie 6/13/2016
这太可怕了......但对我很有帮助,因为我一直在寻找确认我的怀疑,即你不能指望订单!!谢谢@Falanwe
3赞 Royi Mindel 12/7/2016
对于某些人来说,顺序无关紧要 - 只是您浏览了所有密钥的事实。
4赞 Daniel Ballinger 7/20/2011 #11

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

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

所以替换(这不是一个很好的例子,因为 int 没有明确的键)。Dictionary<string, 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];

评论

0赞 takrl 7/20/2011
关于你对密钥的评论,请参阅我对这个问题的后续回答。
2赞 takrl 7/20/2011 #12

为了扩展 Daniels 的帖子和他对密钥的评论,由于密钥无论如何都嵌入在值中,因此您可以使用 a 作为值。这样做的主要原因是,一般来说,Key 不一定可以直接从值派生。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;
2赞 Sharunas Bielskis 3/26/2015 #13

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

2赞 espaciomore 4/7/2016 #14

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

前任。KeyValuePair<string, string>[] filters;

1赞 quicktrick 11/3/2016 #15

Visual Studio 的 UserVoice 提供了指向 dotmore 的泛型 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 中不一定只有成对的键/值。item 数组可以是任意长度,就像在交错数组中一样。