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

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

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

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

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

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

277139 次浏览

你可以这样做:

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

但我不建议你这么做。不能保证最后插入的键位于数组的末尾。键 在 MSDN 上的顺序未指定,可能会更改。在我非常简短的测试中,它似乎是按照插入顺序进行的,但是如果您只是需要知道最新的键,那么您最好像您建议的那样构建一个适当的簿记(尽管我不认为需要基于其他语句的结构)或单变量缓存。

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

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

我认为你可以这样做,语法可能是错误的,一段时间没有使用 C # 去拿最后一样东西

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

或者用 Max 代替 Last 来得到最大值,我不知道哪个更符合你的代码。

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

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

你问这个问题的措辞让我相信字典中的 int 包含了这个条目在字典中的“位置”。从断言密钥没有按照它们被添加的顺序存储来判断,如果这是正确的,那就意味着密钥。点算(或。Count-1,如果您使用从零开始的方式)应该始终是最后输入的键的数字吗?

如果这是正确的,是否有任何理由不能使用 Dictionary < int,string > 以便您可以使用 mydict [ mydict。钥匙。伯爵?

为什么不扩展 dictionary 类来添加最后一个键插入属性呢。比如下面这个?

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

你可以使用 OrderedDictionary

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

Dictionary 是一个哈希表,所以您不知道插入的顺序!

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

如果您决定使用可能被破坏的危险代码,这个扩展函数将根据 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);
}
};

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

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

不应该取决于 Dictionary 中键的顺序。如果您需要订购,您应该使用 OrderedDictionary,如本 回答所建议的。这页上的其他答案也很有趣。

如果键嵌入在值中,一种替代方法是 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];

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

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

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

KeyValuePair<string, string>[] filters;

VisualStudio 的 用户语音通过 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 中不一定只有键/值对。条目数组可以是任意长度,如锯齿数组。