166

我正在使用一个键Dictionary<string, int>int计数。

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

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

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

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

4

15 回答 15

231

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

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

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

于 2011-01-19T13:21:27.887 回答
59

您可以使用OrderedDictionary

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

于 2008-08-08T01:23:22.887 回答
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()so 来克服这个问题时,您必须保留插入的键的有序列表。

于 2009-04-16T14:09:29.563 回答
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);
    }
}
于 2008-08-07T11:15:35.093 回答
6

你总是可以这样做:

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

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

于 2008-08-07T01:13:22.377 回答
5

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

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

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

于 2008-08-07T01:18:25.657 回答
4

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

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

于 2008-08-07T02:38:29.163 回答
4

如果您决定使用易受破坏的危险代码,此扩展函数将根据其内部索引从 a 中获取一个键(对于 Mono 和 .NET,当前似乎与您通过枚举属性Dictionary<K,V>获得的顺序相同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);
    }
};
于 2010-04-12T04:44:58.903 回答
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];
于 2011-07-20T00:45:48.053 回答
3

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

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

于 2008-08-07T02:40:52.747 回答
2

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

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

于 2008-08-07T01:15:55.350 回答
2

要扩展 Daniels 的帖子和他对密钥的评论,因为密钥无论如何都嵌入在值中,您可以求助于使用 aKeyValuePair<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;
于 2011-07-20T09:21:45.130 回答
2

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

于 2015-03-25T18:13:00.467 回答
2

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

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

于 2016-04-06T21:15:54.217 回答
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 中不一定只有一对键/值。项目数组可以是任意长度,例如锯齿状数组。

于 2016-11-03T08:42:21.953 回答