Доступ к словарю.Ключи ключ через числовой индекс
я использую Dictionary<string, int> здесь int - это количество ключей.
теперь мне нужно получить доступ к последнему вставленному ключу внутри словаря, но я не знаю его имени. Очевидная попытка:
int LastCount = mydict[mydict.keys[mydict.keys.Count]];
не работает, потому что Dictionary.Keys не реализует []-индексатора.
мне просто интересно, есть ли подобный класс? Я думал об использовании стека, но это только хранит строку. Теперь я могу создать свою собственную структуру, а затем использовать Stack<MyStruct>, но я интересно, есть ли другая альтернатива, по существу словарь, который реализует []-индексатор на ключах?
15 ответов:
Как указывает @Falanwe в комментарии, делать что-то вроде этого неправильно:
int LastCount = mydict.Keys.ElementAt(mydict.Count -1);вы не должен зависит от порядка ключей в словаре. Если вам нужен заказ, вы должны использовать OrderedDictionary, как предлагается в этой ответ. Другие ответы на этой странице также интересны.
можно использовать OrderedDictionary.
представляет собой набор ключей / значений пары, доступные по ключу или индекс.
словарь-это хэш-таблица, поэтому вы понятия не имеете о порядке вставки!
Если вы хотите знать последний вставленный ключ, я бы предложил расширить словарь, чтобы включить значение 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()поэтому, чтобы преодолеть это, вам нужно будет сохранить упорядоченный список вставленных ключей.
почему бы вам просто не расширить класс словаря, чтобы добавить в последний ключ вставленное свойство. Может быть, что-то вроде следующего?
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); } }
вы всегда можете сделать это:
string[] temp = new string[mydict.count]; mydict.Keys.CopyTo(temp, 0) int LastCount = mydict[temp[mydict.count - 1]]но я бы не рекомендовал его. Нет никакой гарантии, что последний вставленный ключ будет в конце массива. Заказ на ключи на MSDN не указано и может быть изменено. В моем очень коротком тесте это похоже на порядок вставки, но вам было бы лучше строить в правильной бухгалтерии, как стек-как вы предлагаете (хотя я не вижу необходимости в структуре, основанной на ваших других утверждениях)-или один переменный кэш, если вам просто нужно знать Последний ключ.
Я думаю, что вы можете сделать что-то вроде этого, синтаксис может быть неправильным, не использовал C# в то время Чтобы получить последний элемент
Dictionary<string, int>.KeyCollection keys = mydict.keys; string lastKey = keys.Last();или используйте Max вместо Last, чтобы получить максимальное значение, я не знаю, какой из них лучше подходит для вашего кода.
одна альтернатива будет 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];
Я согласен со второй частью ответа Патрика. Даже если в некоторых тестах кажется, что порядок вставки сохраняется, документация (и нормальное поведение для словарей и хэшей) явно указывает, что порядок не определен.
вы просто напрашиваетесь на неприятности в зависимости от заказа ключей. Добавьте свою собственную бухгалтерию (как сказал Патрик, только одну переменную для последнего добавленного ключа), чтобы быть уверенным. Кроме того, не соблазняйтесь всеми методами, такими как Last и Max словарь как таковой, вероятно, относится к ключевому компаратору (я не уверен в этом).
в случае, если вы решите использовать опасный код, который подвержен поломке, эта функция расширения будет извлекать ключ из
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); } };
то, как вы сформулировали вопрос, заставляет меня поверить, что int в словаре содержит "позицию" элемента в словаре. Судя по утверждению, что ключи не хранятся в том порядке, в котором они добавлены, если это правильно, это будет означать, что ключи.Граф (или .Count-1, Если вы используете нулевую базу) все равно всегда должен быть номер последнего введенного ключа?
Если это правильно, есть ли причина, по которой вы не можете использовать словарь
, чтобы вы можете использовать mydict[ mydict.Ключи.Граф ]?
Я не знаю, будет ли это работать, потому что я уверен, что ключи не хранятся в том порядке, в котором они добавлены, но вы можете привести KeysCollection в список, а затем получить Последний ключ в списке... но это стоило бы посмотреть.
единственное, что я могу придумать, это сохранить ключи в списке поиска и добавить ключи в список, прежде чем добавлять их в словарь... это не очень-то красиво.
чтобы расширить пост Дэниелса и его комментарии относительно ключа, так как ключ все равно встроен в значение, вы можете прибегнуть к использованию
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 и его общий аналог. Эти два класса и в ответе Эндрю Питерса упомянутые OrderedDictionary являются классами словарей, в которых элементы могут быть доступны по индексу (позиции), а также по ключу. Как использовать эти классы вы можете найти: Класс SortedList,Sortedlist Generic Class .
словарь может быть не очень интуитивным для использования индекса для ссылки, но вы можете иметь аналогичные операции с массивом KeyValuePair:
ex.
KeyValuePair<string, string>[] filters;
Visual Studio UserVoice дает ссылку на общая реализация OrderedDictionary by dotmore.
но если вам нужно только, чтобы получить пар ключ/значение по индексу и не нужно сделать значения клавиши, вы можете использовать один простой трюк. Объявить универсальный класс (я назвал его 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. Массивы элементов могут быть любой длины, как в многомерном массиве.
Comments