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


160

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

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

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

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

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


1
如果将变量装箱会怎样?
Paul Prewett 2014年

Answers:


222

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

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

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


1
似乎不适用于HashTableSystem.Collections.ICollection',其中不包含“ ElementAt”的定义,也找不到扩展方法“ ElementAt”,该扩展方法接受类型为“ System.Collections.ICollection”的第一个参数
v.oddou

您可以使用ElementAtOrDefaultversion与无例外版本一起使用。
塔里克欧扎贡棍儿

22
看到如此公然的错误答案被接受并大肆宣传,真是令人恐惧。这是错误的,因为如Dictionary<TKey,TValue>文档所述,“中的键顺序Dictionary<TKey, TValue>.KeyCollection未指定”。订单未定义,您无法确定哪个在最后一个位置(mydict.Count -1
Falanwe

这很可怕...但是对我有所帮助,因为我一直在寻找我的怀疑,即您不能指望这份订单!!!感谢@Falanwe
Charlie

3
在某些情况下,顺序不相关-只是您已通过所有键。
Royi Mindel

59

您可以使用OrderedDictionary

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


42
Erhm,在进行19次投票后,没有人提到OrderedDictionary仍然不允许按索引获取密钥吗?
Lazlo

1
您可以使用OrderedDictionary来访问带有整数索引的值,但不能使用System.Collections.Generic.SortedDictionary <TKey,TValue>访问其中的索引必须为TKey的值
Maxence 2012年

OrderedDictionary名称与此收集功能相关,以按添加元素的顺序维护元素。在某些情况下,订单的含义与排序的含义相同,但在此集合中却不一样。
Sharunas Bielskis

18

字典是哈希表,因此您不知道插入顺序!

如果您想知道最后插入的键,建议您将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()来解决此问题时会遇到问题,您将必须保留插入钥匙的有序列表。


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

2
您正在将lastKeyInserted设置为最后插入的值。您是否打算将其设置为插入的最后一个键,或者需要为变量和属性使用更好的名称。
Fantius 2011年

6

您可以始终这样做:

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

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


5

我认为您可以做这样的事情,语法可能是错误的,有一段时间没有使用C#了。

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

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


2
我要补充一点,因为“ Last()”是扩展方法,因此您需要.NET Framework 3.5并在.cs文件顶部添加“ using System.Linq”。
SuperOli 2010年

最后尝试此操作(当显然使用Dist <string,string>时:-) KeyValuePair <string,string> last = oAuthPairs.Last(); 如果(kvp.Key!= last.Key){_oauth_ParamString = _oauth_ParamString +“&”; }
Tim Windsor

4

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

您只是在根据键的顺序问问题。确保添加您自己的簿记(如Patrick所说,最后添加的键只是一个变量)。另外,不要被字典上的Last和Max之类的所有方法所吸引,因为这些方法可能与键比较器有关(我不确定)。


4

万一您决定使用容易破损的危险代码,则此扩展功能将Dictionary<K,V>根据其内部索引(从Mono到.NET当前的顺序与您通过枚举该Keys属性所获得的顺序相同)从a中获取键。)。

最好使用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);
    }
};

嗯,为改善答案而进行的编辑很不满意。我是否不清楚该代码(显然)是丑陋的,应该相应地考虑?
Glenn Slayden '18年

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];

关于您对密钥的评论,请参阅我对此的后续回答。
takrl 2011年

3

您对问题的措辞使我相信词典中的int包含该项在词典中的“位置”。从断言不是按添加顺序存储键的断言来看,如果这是正确的话,这意味着keys.Count(如果使用基于零的数字,则为.Count-1)始终是最后输入的密钥的号码?

如果是正确的话,有什么理由不能代替使用Dictionary <int,string>来使用mydict [mydict.Keys.Count]?


2

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

我唯一能想到的就是将密钥存储在查找列表中,然后再将密钥添加到列表中,然后再将其添加到字典中……这不太好。


@Juan:KeyCollection上没有.Last()方法
lomaxx

我没有测试代码,但是该方法记录在[MSDN] [1]上,也许是框架的另一个版本?[1]:msdn.microsoft.com/en-us/library/bb908406.aspx
胡安

迟到了2年,但可能会对某人有所帮助...请参阅下面我对Juan的回复。Last()是扩展方法。
SuperOli

2

要扩展Daniels post以及他对密钥的评论,由于密钥始终嵌入在值中,因此您可以使用a 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

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

例如 KeyValuePair<string, string>[] filters;


2

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


1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.