Доступ к словарю.Ключи ключ через числовой индекс



я использую Dictionary<string, int> здесь int - это количество ключей.



теперь мне нужно получить доступ к последнему вставленному ключу внутри словаря, но я не знаю его имени. Очевидная попытка:



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


не работает, потому что Dictionary.Keys не реализует []-индексатора.



мне просто интересно, есть ли подобный класс? Я думал об использовании стека, но это только хранит строку. Теперь я могу создать свою собственную структуру, а затем использовать Stack<MyStruct>, но я интересно, есть ли другая альтернатива, по существу словарь, который реализует []-индексатор на ключах?

559   15  

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

    Ничего не найдено.