Каков наилучший способ перебора словаря?

Я видел несколько разных способов перебора словаря в С#. Есть ли стандартный способ?

Ответ 1

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

Ответ 2

Если вы пытаетесь использовать общий словарь в С#, как если бы вы использовали ассоциативный массив на другом языке:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

Или, если вам нужно только перебирать коллекцию ключей, используйте

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

И, наконец, если вас интересуют только значения:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

(Обратите внимание, что ключевое слово var является необязательной функцией С# 3.0 и выше, вы также можете использовать точный тип своих ключей/значений здесь)

Ответ 3

В некоторых случаях вам может понадобиться счетчик, который может быть предоставлен реализацией цикла for. Для этого LINQ предоставляет ElementAt который включает следующее:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

Ответ 4

Зависит от того, хотите ли вы получить ключи или значения...

Из MSDN Dictionary(TKey, TValue) Описание класса:

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}

Ответ 5

Как правило, спрашивать "лучший способ" без определенного контекста - это все равно, что спрашивать, какой цвет лучше?

С одной стороны, есть много цветов и нет лучшего цвета. Это зависит от необходимости и часто от вкуса тоже.

С другой стороны, существует много способов перебора словаря в С#, и не существует лучшего способа. Это зависит от необходимости и часто от вкуса тоже.

Самый простой способ

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Если вам нужно только значение (позволяет назвать его item, более читабельным, чем kvp.Value).

foreach (var item in items.Values)
{
    doStuff(item)
}

Если вам нужен определенный порядок сортировки

Как правило, новички удивляются порядку перечисления словаря.

LINQ предоставляет краткий синтаксис, который позволяет указывать порядок (и многое другое), например:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Опять же, вам может понадобиться только значение. LINQ также предоставляет краткое решение для:

  • итерация непосредственно по значению (позволяет назвать его item, более читаемым, чем kvp.Value)
  • но отсортировано по ключам

Вот:

foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

Из этих примеров можно сделать гораздо больше реальных примеров использования. Если вам не нужен конкретный заказ, просто придерживайтесь "самого простого способа" (см. Выше)!

Ответ 6

Я бы сказал, что foreach является стандартным способом, хотя он, очевидно, зависит от того, что вы ищете

foreach(var kvp in my_dictionary) {
  ...
}

Это то, что вы ищете?

Ответ 7

Вы также можете попробовать это на больших словарях для многопоточной обработки.

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});

Ответ 8

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

Итерация по словарю может быть довольно медленной по сравнению с итерацией по чему-то вроде массива. В моих тестах итерация по массиву занимала 0,015003 секунды, тогда как итерация по словарю (с тем же количеством элементов) занимала 0,0365073 секунды, что в 2,4 раза больше! Хотя я видел гораздо большие различия. Для сравнения, список был где-то между 0,00215043 секундами.

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

Словари оптимизированы для поиска, поэтому, имея в виду, я создал два метода. Один просто делает foreach, другой перебирает ключи, затем смотрит вверх.

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

Этот загружает ключи и перебирает их (я также пытался вытянуть ключи в строку [], но разница была незначительной.

public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}

В этом примере обычный тест foreach занял 0.0310062, а версия ключей - 0.2205441. Загрузка всех ключей и итерация по всем поискам явно намного медленнее!

Для финального теста я выполнил свою итерацию десять раз, чтобы увидеть, есть ли какие-либо преимущества в использовании ключей здесь (к этому моменту мне было просто любопытно):

Вот метод RunTest, если это поможет вам визуализировать происходящее.

private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}

Здесь обычный цикл foreach занимал 0,2820564 секунды (примерно в десять раз дольше, чем одна итерация - как и следовало ожидать). Итерация по клавишам заняла 2,2249449 секунд.

Отредактировано, чтобы добавить: Чтение некоторых других ответов заставило меня задуматься, что произойдет, если я воспользуюсь словарем вместо словаря. В этом примере массив занял 0,0120024 секунды, список 0,0185037 секунд и словарь 0,0465093 секунд. Разумно ожидать, что тип данных влияет на то, насколько медленнее словарь.

Каковы мои выводы?

  • Избегайте итераций по словарю, если вы можете, они существенно медленнее, чем итерации по массиву с теми же данными в нем.
  • Если вы решите перебрать словарь, не пытайтесь быть слишком умным, хотя медленнее вы можете сделать намного хуже, чем при использовании стандартного метода foreach.

Ответ 9

Есть много вариантов. Мой личный фаворит - KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

Вы также можете использовать ключи и коллекции значений

Ответ 10

В С# 7.0 появились деконструкторы, и если вы используете приложение .NET Core 2. 0+, struct KeyValuePair<> уже включает в себя Deconstruct() для вас. Так что вы можете сделать:

var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
    Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}

enter image description here

Ответ 11

С .NET Framework 4.7 можно использовать разложение

var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}

Чтобы этот код работал в более низких версиях С#, добавьте System.ValueTuple NuGet package и напишите где-нибудь

public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
        out T1 key, out T2 value)
    {
        key = tuple.Key;
        value = tuple.Value;
    }
}

Ответ 12

Вы предложили выполнить итерацию

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

FYI, foreach не работает, если значение имеет объект типа.

Ответ 13

Простейшая форма для итерации словаря:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}

Ответ 14

Используя С# 7, добавьте этот метод расширения в любой проект вашего решения:

public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


И используйте этот простой синтаксис

foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


Или этот, если вы предпочитаете

foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


Вместо традиционного

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


Метод расширения преобразует KeyValuePair вашего IDictionary<TKey, TValue> в сильно типизированный tuple, что позволяет использовать этот новый комфортабельный синтаксис.

Он преобразует-просто необходимые словарные записи в tuples, поэтому он НЕ преобразует весь словарь в tuples, поэтому никаких проблем с производительностью, связанных с этим.

Существует только незначительная стоимость вызова метода расширения для создания tuple по сравнению с непосредственным использованием KeyValuePair, что не должно вызывать проблем, если вы все равно назначаете свойства KeyValuePair Key и Value новым переменным цикла.

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

Проверьте это: Блог MSDN - Новые функции в С# 7

Ответ 15

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

foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}

Об этом сообщает этот пост, в котором говорится, что это самый быстрый метод: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html

Ответ 16

Я нашел этот метод в документации для класса DictionaryBase в MSDN:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

Это был единственный, с которым я смог нормально функционировать в классе, унаследованном от DictionaryBase.

Ответ 17

Начиная с С# 7, вы можете деконструировать объекты в переменные. Я считаю, что это лучший способ перебора словаря.

Пример:

Создайте метод расширения в KeyValuePair<TKey, TVal>, который его деконструирует:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
   key = pair.Key;
   value = pair.Value;
}

Выполните итерацию любого Dictionary<TKey, TVal> следующим образом

// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}

Ответ 18

Я воспользуюсь преимуществом .NET 4.0+ и предоставил обновленный ответ на первоначально принятый вариант:

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}

Ответ 19

Стандартный способ перебора словаря в соответствии с официальной документацией на MSDN:

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}

Ответ 20

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

    public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
    {
        foreach (KeyValuePair<T, U> p in d) { a(p); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
    {
        foreach (T t in k) { a(t); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
    {
        foreach (U u in v) { a(u); }
    }

Таким образом, я могу написать код так:

myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
myDictionary.Keys.ForEach(key => Console.Write(key););
myDictionary.Values.ForEach(value => Console.Write(value););

Ответ 21

Если, скажем, вы хотите перебирать коллекцию значений по умолчанию, я считаю, что вы можете реализовать IEnumerable < > , где T - тип объекта значений в словаре, а "this" - словарь.

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}

Ответ 22

var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

Ответ 23

Просто хотел добавить мой 2 цента, так как большинство ответов относятся к foreach-loop. Пожалуйста, взгляните на следующий код:

Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});

Это добавило дополнительный вызов '.ToList()', возможно, будет небольшое улучшение производительности (как указано здесь foreach vs someList.Foreach() {}), espacially при работе с большими словарями и параллельном запуске не является параметром/не будет иметь никакого эффекта вообще.

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

Когда вы просто хотите "читать" клавиши и значения, вы также можете использовать IEnumerable.Select().

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

Ответ 24

Я написал расширение для цикла над словарем.

public static class DictionaryExtension
{
    public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
        foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
            action(keyValue.Key, keyValue.Value);
        }
    }
}

Тогда вы можете позвонить

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));

Ответ 25

Не стандартный способ, но работающий:

Dictionary<string, int> dict = new Dictionary<string, int>
{
    {"Foo", 1 },
    {"Bar", 2 }
};

dict.Select(kvp => { Console.WriteLine(kvp.Key + " - " + kvp.Value.ToString()); return kvp; }).Count();

Ответ 26

Dictionary <TKey, TValue> Это универсальный класс коллекции в С#, в котором хранятся данные в формате значения ключа. Ключ должен быть уникальным и не может быть нулевым, в то время как значение может быть дублированным и нулевым. Поскольку каждый элемент в словаре рассматривается как структура KeyValuePair <TKey, TValue>, представляющая ключ и его значение. и, следовательно, мы должны взять тип элемента KeyValuePair <TKey, TValue> во время итерации элемента. Ниже приведен пример.

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}

Ответ 27

Три разных метода перебора словаря.

Метод 1-использование для цикла

for (int i = 0; i < DemotestDictionary.Count; i++ )
 {
 int key = i;
 string value = DemotestDictionary[i];
 // Do something here
 }

Метод 2 с использованием итерации foreach

foreach (var entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
 // Do something here
 } 

Метод 3-foreach итерация с использованием KeyValuePair

foreach (KeyValuePair<int, string> entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
// Do something here
 }

Ответ 28

в дополнение к постам с самым высоким рейтингом, где есть обсуждение между использованием

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

или же

foreach(var entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

наиболее полным является следующее, потому что вы можете видеть тип словаря из инициализации, kvp - KeyValuePair

var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x

foreach(var kvp in myDictionary)//iterate over dictionary
{
    // do something with kvp.Value or kvp.Key
}

Ответ 29

Словари являются специальными списками, в то время как каждое значение в списке имеет ключ, который также является переменной. Хорошим примером словаря является телефонная книга.

   Dictionary<string, long> phonebook = new Dictionary<string, long>();
    phonebook.Add("Alex", 4154346543);
    phonebook["Jessica"] = 4159484588;

Обратите внимание, что при определении словаря нам нужно предоставить общее определение с двумя типами - типом ключа и типом значения. В этом случае ключ является строкой, а значение является целым числом.

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

Чтобы проверить, есть ли в словаре определенный ключ, мы можем использовать метод ContainsKey:

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

if (phonebook.ContainsKey("Alex"))
{
    Console.WriteLine("Alex number is " + phonebook["Alex"]);
}

Чтобы удалить элемент из словаря, мы можем использовать метод Remove. Удаление элемента из словаря по его ключу - это быстро и очень эффективно. При удалении элемента из списка с использованием его значения процесс идет медленно и неэффективно, в отличие от функции удаления из словаря.

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

phonebook.Remove("Jessica");
Console.WriteLine(phonebook.Count);