我正在使用Dictionary,其中int是键的计数。
现在,我需要访问字典中最后插入的键,但是我不知道它的名称。 显而易见的尝试:
1
| int LastCount = mydict[mydict.keys[mydict.keys.Count]]; |
不起作用,因为Dictionary.Keys没有实现[] -indexer。
我只是想知道是否有类似的课程? 我考虑过使用堆栈,但是只存储一个字符串。 我现在可以创建自己的结构,然后使用Stack,但是我想知道是否还有另一种选择,本质上是在Keys上实现[] -indexer的Dictionary?
正如@Falanwe在评论中指出的那样,这样做是不正确的:
1
| int LastCount = mydict.Keys.ElementAt(mydict.Count -1); |
您不应该依赖于字典中键的顺序。如果需要订购,则应使用此答案中建议的OrderedDictionary。此页面上的其他答案也很有趣。
您可以使用OrderedDictionary。
Represents a collection of key/value
pairs that are accessible by the key
or index.
字典是哈希表,因此您不知道插入顺序!
如果您想知道最后插入的键,建议您将Dictionary扩展为包含LastKeyInserted值。
例如。:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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()时,您会遇到问题,因此要解决此问题,必须保留插入键的有序列表。
为什么不扩展字典类以添加最后一个插入键的属性。像下面这样的东西?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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);
}
} |
您可以始终这样做:
1 2 3
| string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]] |
但是我不推荐它。不能保证最后插入的键将在数组的末尾。 MSDN上的密钥顺序未指定,并且可能会发生变化。在我的简短测试中,它似乎是按插入顺序排列的,但是您最好像堆栈一样构建适当的簿记-正如您所建议的(尽管我认为您并不需要基于您的结构其他语句)或单个变量缓存(如果您只需要知道最新密钥)。
我认为您可以执行以下操作,语法可能有误,有一段时间没有使用C#
获取最后一个项目
1 2
| Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last(); |
还是使用Max而不是Last来获得最大值,我不知道哪个更适合您的代码。
如果将密钥嵌入值中,则可以选择使用KeyedCollection。
只需在密封类中创建一个基本实现即可使用。
因此要替换Dictionary(这不是一个很好的例子,因为没有用于int的明确键)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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]; |
万一您决定使用容易破损的危险代码,此扩展功能将根据Dictionary的内部索引(对于Mono和.NET,当前的显示顺序与您获取的顺序相同)从其获取密钥。枚举Keys属性)。
最好使用Linq:dict.Keys.ElementAt(i),但是该函数将迭代O(N);以下是O(1),但反射性能下降。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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);
}
}; |
我同意帕特里克回答的第二部分。即使在某些测试中似乎保持插入顺序,文档(以及字典和哈希的正常行为)也明确指出未指定顺序。
您只是在根据键的顺序问问题。确保添加您自己的簿记(如Patrick所说,最后添加的键只是一个变量)。另外,不要被字典上的Last和Max之类的所有方法所吸引,因为这些方法可能与键比较器有关(我不确定)。
您对问题的措辞使我相信词典中的int包含该项在词典中的"位置"。从断言不是按添加顺序存储键的断言来看,如果这是正确的话,这意味着keys.Count(如果使用从零开始,则为.Count-1)始终是最后输入的密钥的号码?
如果是正确的话,有什么理由不能代替Dictionary 来使用mydict [mydict.Keys.Count]?
字典对于使用索引作为参考可能不是很直观,但是您可以对KeyValuePair数组进行类似的操作:
恩。
KeyValuePair[] filters;
您还可以使用SortedList及其通用副本。这两个类以及在Andrew Peters回答中提到的OrderedDictionary都是字典类,在其中可以通过索引(位置)和键来访问项目。如何使用这些类,您可以找到:SortedList类,SortedList通用类。
要扩展Daniels的帖子及其对键的评论,由于键始终嵌入在值中,因此您可以使用KeyValuePair作为值。这样做的主要原因是,通常,键不一定直接从值派生。
然后看起来像这样:
1 2 3 4 5 6 7 8
| public sealed class CustomDictionary<TKey, TValue>
: KeyedCollection<TKey, KeyValuePair<TKey, TValue>>
{
protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
{
return item.Key;
}
} |
要像前面的示例一样使用它,您需要执行以下操作:
1 2 3 4 5 6 7
| 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; |
我不知道这是否行得通,因为我非常确定密钥没有按照添加顺序存储,但是您可以将KeysCollection强制转换为列表,然后获取列表中的最后一个密钥...但值得一看。
我唯一想到的另一件事是将密钥存储在查找列表中,然后将密钥添加到列表中,然后再将其添加到字典中……这不太好。
Visual Studio的UserVoice通过dotmore提供了到通用OrderedDictionary实现的链接。
但是,如果您只需要按索引获取键/值对,而不必按键获取值,则可以使用一个简单的技巧。声明一些通用类(我称之为ListArray),如下所示:
1
| class ListArray< T > : List<T[]> { } |
您也可以使用构造函数声明它:
1 2 3 4 5
| class ListArray< T > : List<T[]>
{
public ListArray() : base() { }
public ListArray(int capacity) : base(capacity) { }
} |
例如,您从文件中读取了一些键/值对,并且只想按照它们被读取的顺序存储它们,以便稍后通过索引获取它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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中不一定只需要一对键/值。项数组可以是任何长度,例如锯齿状数组。