Доступ к словарю.Ключи Ввод ключа с помощью числового индекса

StackOverflow https://stackoverflow.com/questions/4227

  •  08-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Это было полезно?

Решение

Как указывает @Falanwe в комментарии, делать что-то подобное - это неверный:

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

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

Другие советы

Вы можете использовать Упорядоченный справочник.

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

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

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

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

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

Одной из альтернатив было бы Ключевая коллекция если ключ встроен в значение.

Просто создайте базовую реализацию в закрытом классе для использования.

Итак, чтобы заменить 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 (или .Count - 1, если вы используете нулевое значение) по-прежнему всегда должен быть номером последнего введенного ключа?

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

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

Словарь может быть не очень интуитивно понятным для использования индекса в качестве справочного, но вы можете выполнять аналогичные операции с массивом Ключевая пара значений:

бывший.KeyValuePair<string, string>[] filters;

Visual Studio - это Пользовательский голос дает ссылку на общая реализация OrderedDictionary автор: Дотмор.

Но если вам нужно получить пары ключ / значение только по индексу и не нужно получать значения по ключам, вы можете использовать один простой трюк.Объявите некоторый универсальный класс (я назвал его 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.Массивы элементов могут быть любой длины, как в jagged array.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top