我正在使用Dictionary<string, int>
,其中int
是密钥的计数。
现在,我需要访问Dictionary中最后插入的Key,但我不知道它的名字。显而易见的尝试:
int LastCount = mydict[mydict.keys[mydict.keys.Count]];
不起作用,因为Dictionary.Keys
没有实现[] -indexer。
我只是想知道是否有类似的类?我想过使用Stack,但只存储一个字符串。我现在可以创建自己的结构然后使用Stack<MyStruct>
,但我想知道是否还有另一种选择,本质上是一个在Keys上实现[] -indexer的字典?
答案 0 :(得分:214)
正如@Falanwe在评论中指出的那样,做这样的事情 不正确 :
int LastCount = mydict.Keys.ElementAt(mydict.Count -1);
你不应该依赖于词典中键的顺序。如果您需要订购,则应使用此OrderedDictionary中建议的answer。此页面上的其他答案也很有趣。
答案 1 :(得分:56)
您可以使用OrderedDictionary。
表示键/值的集合 密钥可访问的对 或指数。
答案 2 :(得分:17)
字典是一个哈希表,所以你不知道插入的顺序!
如果你想知道最后插入的密钥,我建议扩展Dictionary以包含LastKeyInserted值。
E.g:
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()
时,你会遇到问题,所以要克服这个问题,你必须保留一个有序的键列表。
答案 3 :(得分: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);
}
}
答案 4 :(得分:6)
您可以随时执行此操作:
string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]
但我不推荐它。无法保证最后插入的密钥位于数组的末尾。密钥on MSDN的排序未指定,可能会发生变化。在我非常简短的测试中,它看起来似乎是按照插入的顺序,但你最好建立像堆栈一样的正确簿记 - 正如你的建议(尽管我没有看到基于你的结构的需要)其他语句) - 如果您只需要知道最新的密钥,则使用单变量缓存。
答案 5 :(得分:5)
我认为你可以做这样的事情,语法可能是错误的,暂时没有使用过C# 获取最后一项
Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();
或使用Max而不是Last来获取最大值,我不知道哪种更适合您的代码。
答案 6 :(得分:4)
我同意帕特里克回答的第二部分。即使在某些测试中它似乎保持了插入顺序,文档(以及字典和散列的正常行为)明确指出排序是未指定的。
根据按键的顺序,你只是在寻找麻烦。添加你自己的簿记(正如帕特里克所说,只是最后一个添加键的一个变量),以确保。另外,不要被字典中的Last和Max等所有方法所诱惑,因为这些方法可能与关键比较器有关(我不确定)。
答案 7 :(得分:4)
如果您决定使用可能破损的危险代码,此扩展函数将根据其内部索引从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);
}
};
答案 8 :(得分: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];
答案 9 :(得分:3)
你提出这个问题的方式让我相信字典中的int包含了字典中项目的“位置”。判断密钥没有按照添加的顺序存储,如果这是正确的,那就意味着keys.Count(或.Count - 1,如果你使用从零开始)应该仍然始终是最后输入的密钥的编号?
如果这是正确的,是否有任何理由你不能使用Dictionary&lt; int,string&gt;这样你就可以使用mydict [mydict.Keys.Count]了?
答案 10 :(得分:2)
我不知道这是否可行,因为我很确定密钥没有按照添加的顺序存储,但您可以将KeysCollection强制转换为List,然后获取列表中的最后一个密钥......但值得一看。
我能想到的另一件事就是将密钥存储在查找列表中,然后在将它们添加到字典之前将密钥添加到列表中......这不是很好。
答案 11 :(得分:2)
要扩展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;
答案 12 :(得分:2)
您也可以使用SortedList及其Generic对应物。这两个类和安德鲁·彼得斯回答提到的OrderedDictionary是字典类,其中的项目可以通过索引(位置)和键来访问。如何使用这些课程:SortedList Class,SortedList Generic Class。
答案 13 :(得分:2)
使用索引作为参考可能不是很直观,但是,您可以使用 KeyValuePair 数组进行类似的操作:
离。
KeyValuePair<string, string>[] filters;
答案 14 :(得分:1)
Visual Studio的UserVoice通过dotmore提供了generic OrderedDictionary implementation的链接。
但是如果您只需要通过索引获取键/值对并且不需要通过键获取值,则可以使用一个简单的技巧。声明一些泛型类(我称之为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中的键/值对。项目数组可以是任何长度,如锯齿状数组。