提问人:Michael Stum 提问时间:8/7/2008 最后编辑:user2771704Michael Stum 更新时间:1/3/2020 访问量:279469
通过数字索引访问 Dictionary.Keys 键
Accessing a Dictionary.Keys Key through a numeric index
问:
我正在使用 where the 是键的计数。Dictionary<string, int>
int
现在,我需要访问字典中最后插入的键,但我不知道它的名称。显而易见的尝试:
int LastCount = mydict[mydict.keys[mydict.keys.Count]];
不起作用,因为不实现 [] 索引器。Dictionary.Keys
我只是想知道有没有类似的课程?我想过使用堆栈,但它只存储一个字符串。我现在可以创建自己的结构,然后使用 ,但我想知道是否有另一种选择,本质上是一个在 Keys 上实现 [] 索引器的 Dictionary?Stack<MyStruct>
答:
你总是可以这样做:
string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]
但我不会推荐它。不能保证最后插入的键位于数组的末尾。MSDN 上密钥的顺序未指定,可能会更改。在我非常简短的测试中,它似乎是按插入顺序排列的,但你最好像堆栈一样在适当的簿记中构建——正如你所建议的那样(尽管我认为不需要基于你的其他语句的结构)——或者如果你只需要知道最新的键,则使用单个变量缓存。
我不知道这是否可行,因为我非常确定键不是按照添加顺序存储的,但是您可以将 KeysCollection 转换为列表,然后获取列表中的最后一个键......但值得一看。
我唯一能想到的另一件事是将键存储在查找列表中,并在将它们添加到字典之前将键添加到列表中......这并不漂亮。
评论
我认为你可以做这样的事情,语法可能是错误的,有一段时间没有使用过 C# 获取最后一项
Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();
或者使用 Max 而不是 Last 来获取最大值,我不知道哪一个更适合您的代码。
评论
我同意帕特里克回答的第二部分。即使在某些测试中它似乎保持插入顺序,文档(以及字典和哈希的正常行为)也明确指出未指定顺序。
你只是在自找麻烦,这取决于钥匙的顺序。添加您自己的簿记(正如 Patrick 所说,只是最后一个添加的键的单个变量)以确保。另外,不要被字典上的 Last 和 Max 等所有方法所诱惑,因为这些方法可能与键比较器有关(我不确定)。
你提出问题的方式让我相信字典中的 int 包含项目在字典中的“位置”。从密钥未按添加顺序存储的断言来看,如果这是正确的,则意味着密钥。计数(或 .计数 - 1,如果您使用的是从零开始的)仍应始终是最后输入的键的编号?
如果这是正确的,那么有什么理由不能改用 Dictionary<int, string>以便您可以使用 mydict[ mydict.Keys.Count ]?
为什么不扩展字典类以添加最后一个键插入属性。也许像下面这样?
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 以包含 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()
如果您决定使用容易损坏的危险代码,此扩展函数将根据其内部索引从 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);
}
};
评论
正如@Falanwe在评论中指出的那样,做这样的事情是不正确的:
int LastCount = mydict.Keys.ElementAt(mydict.Count -1);
不应依赖于字典中键的顺序。如果需要排序,则应使用 OrderedDictionary,如本答案中所示。此页面上的其他答案也很有趣。
评论
HashTable
ElementAtOrDefault
Dictionary<TKey,TValue>
文档所述“未指定 中的键的顺序”。顺序未定义,您无法确定哪个在最后一个位置(Dictionary<TKey, TValue>.KeyCollection
mydict.Count -1
)
如果键嵌入在值中,则另一种选择是 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];
评论
为了扩展 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;
还可以使用 SortedList 及其对应的 Generic。这两个类,在Andrew Peters的回答中提到的OrderedDictionary是字典类,其中可以通过索引(位置)和键访问项目。如何使用这些类,您可以找到: SortedList 类 , SortedList 泛型类 。
字典对于使用索引作为参考可能不是很直观,但是,您可以对 KeyValuePair 数组进行类似的操作:
前任。KeyValuePair<string, string>[] filters;
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 数组可以是任意长度,就像在交错数组中一样。
评论