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

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

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

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

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

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

7.08.2008 00:51:21
Что произойдет, если вы поместите эту переменную в коробку?
Paul Prewett 29.07.2014 19:24:22
15 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

221
3.01.2020 07:03:49
кажется, не работает с HashTableSystem.Collections.ICollection ', не содержит определения для' ElementAt 'и не найден метод расширения' ElementAt ', принимающий первый аргумент типа' System.Collections.ICollection '
v.oddou 20.02.2015 06:59:10
Вы можете использовать ElementAtOrDefaultверсию для работы с исключительной версией.
Tarık Özgün Güner 25.02.2015 20:04:56
Страшно видеть, что такой откровенно неправильный ответ принят и так сильно проголосовал. Это неправильно, потому что, как говорится в Dictionary<TKey,TValue>документации, «порядок ключей в Dictionary<TKey, TValue>.KeyCollectionне указан». mydict.Count -1
Falanwe 10.06.2016 16:28:55
Это страшно ... но полезно для меня, так как я искал подтверждение моего подозрения, что вы не можете рассчитывать на заказ !!! Спасибо @Falanwe
Charlie 13.06.2016 00:47:47
Для некоторых порядок не актуален - только то, что вы прошли все ключи.
Royi Mindel 7.12.2016 09:15:40

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

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

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

6
3.01.2020 06:24:59

Я не знаю, сработает ли это, потому что я почти уверен, что ключи хранятся не в том порядке, в котором они были добавлены, но вы можете привести KeysCollection к списку, а затем получить последний ключ в списке ... но стоило бы посмотреть.

Единственное, о чем я могу думать, это сохранить ключи в списке поиска и добавить ключи в список, прежде чем добавлять их в словарь ... это не очень хорошо.

2
7.08.2008 01:15:55
@Juan: в KeyCollection нет метода .Last ()
lomaxx 7.08.2008 01:23:35
Я не тестировал код, но метод описан в [MSDN] [1], может быть, это еще одна версия фреймворка? [1]: msdn.microsoft.com/en-us/library/bb908406.aspx
Juan 7.08.2008 02:13:25
На 2 года позже, но это может кому-то помочь ... см. Мой ответ на пост Хуана ниже. Last () - это метод расширения.
SuperOli 5.11.2010 14:08:58

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

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

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

5
7.08.2008 01:18:25
Я хотел бы добавить, что поскольку Last () является методом расширения, вам потребуется .NET Framework 3.5 и добавить «using System.Linq» вверху файла .cs.
SuperOli 5.11.2010 14:07:18
Попробуйте это в последний раз (при использовании Dist <string, string> очевидно :-) KeyValuePair <string, string> last = oAuthPairs.Last (); if (kvp.Key! = last.Key) {_oauth_ParamString = _oauth_ParamString + "&"; }
Tim Windsor 24.06.2013 18:23:13

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

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

4
11.09.2011 16:57:02

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

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

3
7.08.2008 02:40:52

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

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);
    }
}
8
11.08.2011 12:46:41
Вы устанавливаете lastKeyInserted для последнего вставленного значения. Либо вы хотите установить последний ключ, либо вам нужны более подходящие имена для переменной и свойства.
Fantius 2.03.2011 00:48:10

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

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

59
3.01.2020 06:23:25
Эмм, после 19 голосов никто не упомянул, что OrderedDictionary до сих пор не позволяет получить ключ по индексу?
Lazlo 14.07.2011 02:21:51
Вы можете получить доступ к значению с целочисленным индексом с помощью OrderedDictionary , но не с помощью System.Collections.Generic.SortedDictionary <TKey, TValue>, где индекс должен быть TKey
Maxence 5.10.2012 12:56:21
Имя OrderedDictionary связано с этой функцией коллекции, чтобы элементы были сохранены в том же порядке, в котором они были добавлены. В некоторых случаях заказ имеет то же значение, что и сортировка, но не в этой коллекции.
Sharunas Bielskis 4.01.2020 08:06:32

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

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

18
6.09.2014 20:19:42

Если вы решите использовать опасный код, который может быть поврежден, эта функция расширения будет извлекать ключ из a в 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
2.01.2018 18:13:28
Хм, редактирование для улучшения ответа заработало понижательную оценку. Разве я не дал понять, что код (очевидно) отвратителен и должен рассматриваться соответствующим образом?
Glenn Slayden 2.01.2018 21:38:06

Одной из альтернатив будет 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
3.01.2020 06:24:33
Что касается ваших комментариев к ключу, см. Мой ответ на этот вопрос.
takrl 20.07.2011 09:22:29

Чтобы подробнее остановиться на публикации Дэниелса и его комментариях относительно ключа, поскольку ключ в любом случае встроен в значение, вы можете прибегнуть к использованию в 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;
2
20.07.2011 09:35:50

Вы также можете использовать SortedList и его общий аналог. Эти два класса и упомянутый в ответе Эндрю Питерса OrderedDictionary являются словарными классами, в которых элементы могут быть доступны по индексу (позиции), а также по ключу. Как использовать эти классы вы можете найти: SortedList Class , SortedList Generic Class .

2
3.01.2020 06:39:03

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

ех. KeyValuePair<string, string>[] filters;

2
6.04.2016 21:15:54

UserVoice Visual Studio дает ссылку на универсальную реализацию OrderedDictionary от 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. Массивы элементов могут быть любой длины, например, в зубчатом массиве.

1
3.11.2016 08:58:42