Доступ к ключу Dictionary.Keys через числовой индекс - PullRequest
147 голосов
/ 07 августа 2008

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

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

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

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

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

Ответы [ 15 ]

214 голосов
/ 19 января 2011

Как отмечает @Falanwe в комментарии, что-то вроде этого неверно :

int LastCount = mydict.Keys.ElementAt(mydict.Count -1);

Вы не должны зависеть от порядка ключей в словаре. Если вам нужно упорядочить, вы должны использовать OrderedDictionary , как предложено в этом ответе . Другие ответы на этой странице также интересны.

56 голосов
/ 08 августа 2008

Вы можете использовать OrderedDictionary .

Представляет коллекцию ключ / значение пары, которые доступны по ключу или индекс.

17 голосов
/ 16 апреля 2009

Словарь - это хеш-таблица, поэтому вы не знаете, как вставить!

Если вы хотите узнать последний вставленный ключ, я бы предложил расширить словарь, включив в него значение 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() вы столкнетесь с проблемами, поэтому для преодоления этого вам потребуется сохранить упорядоченный список вставленных ключей.

8 голосов
/ 07 августа 2008

Почему бы вам просто не расширить класс словаря, чтобы добавить в него вставленное свойство последнего ключа. Может быть, что-то вроде следующего?

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);
    }
}
6 голосов
/ 07 августа 2008

Вы всегда можете сделать это:

string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]

Но я бы не рекомендовал это. Нет гарантии, что последний вставленный ключ будет в конце массива. Заказ ключей на MSDN не указан и может быть изменен. В моем очень коротком тесте это, кажется, в порядке вставки, но вам лучше бы построить правильную бухгалтерию, например, стеком - как вы предлагаете (хотя я не вижу необходимости в структуре, основанной на вашем другие операторы) - или кеш одной переменной, если вам просто нужно знать последний ключ.

5 голосов
/ 07 августа 2008

Я думаю, что вы можете сделать что-то вроде этого, синтаксис может быть неправильным, не использовал C # некоторое время Чтобы получить последний пункт

Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();

или используйте Max вместо Last, чтобы получить максимальное значение, я не знаю, какой из них лучше подходит вашему коду.

4 голосов
/ 20 июля 2011

Одной альтернативой будет 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];
4 голосов
/ 12 апреля 2010

Если вы решите использовать опасный код, который может быть поврежден, эта функция расширения будет извлекать ключ из 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);
    }
};
4 голосов
/ 07 августа 2008

Я согласен со второй частью ответа Патрика. Даже если в некоторых тестах, похоже, сохраняется порядок вставки, в документации (и нормальном поведении для словарей и хэшей) порядок явно не указан.

Вы просто напрашиваетесь на неприятности в зависимости от порядка клавиш. Чтобы быть уверенным, добавьте свою собственную бухгалтерию (как сказал Патрик, только одну переменную для последнего добавленного ключа). Кроме того, не поддавайтесь соблазну всеми методами, такими как Last и Max в словаре, поскольку они, вероятно, связаны с ключевым компаратором (я не уверен в этом).

3 голосов
/ 07 августа 2008

То, как вы сформулировали вопрос, приводит меня к мысли, что int в Словаре содержит «позицию» элемента в Словаре. Судя по утверждению, что ключи хранятся не в том порядке, в котором они были добавлены, если это правильно, это будет означать, что keys.Count (или .Count - 1, если вы используете нули) должны всегда будет номер последнего введенного ключа?

Если это правильно, есть ли причина, по которой вы не можете вместо этого использовать Dictionary , чтобы вы могли использовать mydict [mydict.Keys.Count]?

...