Как лучше всего перебирать словарь?

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

Я удивлен, что на этот вопрос есть много ответов, а также один, за который проголосовали 923 раза .. (конечно, использует foreach). Я бы поспорил или, по крайней мере, добавил, что если вам нужно перебирать словарь, скорее всего, вы используя его неправильно / неуместно ... Мне пришлось сделать этот комментарий, потому что я видел, как словари злоупотребляли способами, которые, ИМХО, не подходили ... Да, могут быть редкие обстоятельства, когда вы повторяете словарь, а не выполняете поиск, а это то, что он предназначен для .. Пожалуйста, имейте это в виду, прежде чем задумываться, как перебирать словарь.

Vikas Gupta 18.09.2014 10:52

@VikasGupta Что бы вы посоветовали сделать с коллекцией пар ключ-значение, если вы не знаете, какие будут ключи?

nasch 27.02.2015 01:27

@nasch: myDictionary.Keys предоставит вам коллекцию, содержащую ключи в myDictionary.

displayName 28.10.2015 16:47

@displayName Если вы хотите что-то сделать с каждой парой ключ-значение, но у вас нет ссылки на ключи для поиска значений, вы бы перебирали словарь, верно? Я просто указывал, что могут быть моменты, когда вы захотите это сделать, несмотря на заявление Викаса о том, что это обычно неправильное использование.

nasch 28.10.2015 21:01

Сказать, что это неправильное использование, означает, что есть лучшая альтернатива. Что это за альтернатива?

Kyle Delaney 21.06.2017 00:13

VikasGupta ошибается, я могу утверждать это после многих лет высокопроизводительного программирования на C# и C++ в не теоретических сценариях. Действительно, часты случаи, когда можно создать словарь, сохранить уникальные пары ключ-значение, а затем перебрать эти значения, которые, как доказано, имеют уникальные ключи в коллекции. Создание любых дополнительных коллекций - действительно неэффективный и дорогостоящий способ избежать повторения словаря. Пожалуйста, предоставьте хорошую альтернативу в качестве ответа на вопрос, разъясняющий вашу точку зрения, иначе ваш комментарий будет довольно бессмысленным.

sɐunıɔןɐqɐp 01.08.2018 23:53

Также для отладки и попытки выяснить, что находится в вашем словаре, когда что-то идет не так. Это очень полезно

MingMan 10.09.2018 19:30

VikasGupta на 100% правильно. Если у вас есть «набор пар ключ-значение» и вы не знаете, что с ним делать, вы можете буквально поместить его в ICollection<KeyValuePair> (самая простая реализация: List). И если вас беспокоит "высокопроизводительное программирование", то вы должны знать, что единственное, в чем словари быстрее, - это поиск элемента с помощью ключа - добавление элементов медленнее, а итерация по словарю может легко занять в два раза больше времени, чем итерация по списку.

Raphael Schmitz 12.03.2019 19:53

Есть еще три способа сделать это: 1- для цикла 2- Parallel Enumerable.ForAllMethod 3- String.Join Вы можете увидеть образец их кода по этой ссылке: techiedelight.com/iterate-over-dictionary-csharp

elnaz jangi 12.06.2020 16:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2 820
9
1 794 310
30
Перейти к ответу Данный вопрос помечен как решенный

Ответы 30

Ответ принят как подходящий

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

Что делать, если я точно не знаю тип ключа / значения в словаре. В этом случае лучше использовать var entry, и поэтому я проголосовал за этот ответ во втором взгляде вместо предыдущего.

Ozair Kafray 05.01.2016 10:32

@OzairKafray, использующий var, когда вы не знаете тип, обычно является плохой практикой.

Nate 26.01.2016 02:34

Этот ответ лучше, потому что Пабло по умолчанию не использовал ленивый кодировщик var, который скрывает возвращаемый тип.

MonkeyWrench 17.05.2016 20:19

@MonkeyWrench: Мех. Visual Studio знает, что это за тип; все, что вам нужно сделать, это навести указатель мыши на переменную, чтобы узнать.

Robert Harvey 09.12.2016 21:30

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

quemeful 07.02.2017 18:58

Насколько я понимаю, var работает только в том случае, если тип известен во время компиляции. Если Visual Studio знает тип, вы также можете узнать об этом.

Kyle Delaney 21.09.2017 16:06

Одна важная причина, по которой вы всегда должны использовать var в операторе foreach, заключается в том, что, если вы этого не сделаете, трудно увидеть, есть ли явное приведение, скрытое в синтаксисе, или нет, это может привести к ошибке! Этого не происходит, когда типом времени компиляции myDic является общий Dictionary<,>, потому что тип времени компиляции Current является структурой, которая не допускает таких приведений. Но установите IDictionary myDic2 = myDic;, тогда foreach(KeyValuePair<string, string> entry in myDic2) будет компилироваться, но при запуске выдаст ошибку (если myDic не пуст). Также: IList<IAnimal> s = ...; foreach (IPlant p in s) {}

Jeppe Stig Nielsen 20.11.2017 12:39

@JeppeStigNielsen, допустим, я использовал var в foreach, вы планируете использовать его ключ и значения позже или просто повторяете? если вы планируете использовать его позже, вам нужно знать фактический тип переменной, если все ваши переменные не объявлены как vars! Строгое объявление типа упрощает чтение кода в электронной почте, блокноте и системе контроля версий, в которых также нет IntelliSense. Я согласен с nate и MonkeyRench

AaA 29.12.2017 04:37

@AaA Почти всегда мы используем итерационную переменную foreach внутри тела цикла. Если вам нужен удобный для «Блокнота» код, я предлагаю вам ввести тот же тип, который выводит IntelliSense при использовании var. Затем, если вам нужно приведение, напишите его явно. Это, конечно, только мое мнение.

Jeppe Stig Nielsen 29.12.2017 12:25

Я сумасшедший, когда использую var только для запросов linq, анонимных типов и назначений, которые заканчиваются вызовом конструктора? Повышает удобочитаемость, избегает побочных эффектов во время выполнения.

Gusdor 14.01.2018 03:19

@Gusdor var не влияет на поведение во время выполнения. И да, вы с ума сошли :) <3

Aluan Haddad 22.01.2018 17:02

@AluanHaddad рассмотрите этот код: var items = externalLibrary.GetItems(filestream); Вы закрываете поток и перебираете коллекцию в цикле. В новой версии «Внешней библиотеки» тип возвращаемого значения GetItems изменен с List<string> на IEnumerable<string>. БАХ, исключение. Доступ к файлу осуществляется после того, как вы закрыли поток. Поведение во время выполнения изменилось, потому что вам было лень набрать IList<string>. Зачем ждать тестов, чтобы найти это, если ваш компилятор может это сделать?

Gusdor 22.01.2018 17:30

@Gusdor var часто больше ориентирован на удобочитаемость, чем на возможность записи. Когда я читаю метод, я хочу начать с понимания на высоком уровне. Затем при необходимости углубляюсь в детали. В вашем примере изменение возвращаемого типа не означает, что базовая реализация является ленивой. В любом случае, с такими API неудобно работать, потому что уже неясно, кто отвечает за удаление потока. В любом случае, я не говорю никогда не использовать явный тип, я говорю, что предпочитаю var почти везде. Java заставляет мои глаза кровоточить, когда мне нужно его читать, а не только написать.

Aluan Haddad 22.01.2018 18:18

@AluanHaddad Я только что описал вам, как удобочитаемость может повлиять на правильность вашего программного обеспечения с течением времени .... Я также согласен с тем, что код читается больше, чем пишется, и все же var менее читабелен. Почему удаление всякого смысла могло быть более читабельным? Рассмотрим var userInfo = service.GetUser();. Какой тип userInfo? Без intellisense не знаешь. Если вы просматривали это в веб-интерфейсе Github, вам будет сложно ориентироваться.

Gusdor 22.01.2018 18:35

@Gusdor, мы на грани священной войны;). Честно говоря, я не хочу знать и думать о том, что это за тип. Я просто хочу знать, что это какое-то представление пользователя. В любом случае выбор за вами и за мной. Я считаю, что var более читабелен.

Aluan Haddad 22.01.2018 18:37

@Gusdor Разве это не критическое изменение? Я ожидаю, что авторы «Внешней библиотеки» будут достаточно ответственными, чтобы описать ее в примечаниях к выпуску, чтобы я мог найти все ссылки на GetItems в моем коде и соответствующим образом изменить их. И если авторы недостаточно ответственны за это, то пора найти новую «Внешнюю библиотеку».

Zev Spitz 24.01.2018 04:26

@Gusdor Рассмотрим var userInfo = service.GetUser();. Какой тип userInfo? - Ответ: когда мне до этого дела? Когда я пишу код в Visual Studio, мне нужно знать о свойствах и методах, доступных мне из типа. На GitHub мне, вероятно, следует избегать редактирования кода без защиты VS, компилятора и Intellisense. Для чтения кода на GitHub, я думаю, будет лучше настаивать на том, чтобы с GitHub синхронизировался только компилируемый код, и поэтому у меня достаточно информации о userInfo из окружающего кода, использующего его свойства и методы ...

Zev Spitz 24.01.2018 04:35

... чтобы получить хорошее представление о том, что это такое, даже если я не знаю точного типа. В этом сценарии наличие конкретного имени типа скрывает назначение кода - userInfo является экземпляром MyCompany.MyProject.InnerNamespace.UserDetails; без просмотра соответствующего файла (а у меня нет ссылки Goto на GitHub, поэтому мне придется искать его вручную), это практически не добавляет информации; и поэтому имя становится еще одним шумом, который я должен разбирать во время чтения. Только мои 2 цента.

Zev Spitz 24.01.2018 04:39

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

dynamichael 10.06.2018 17:10

обсуждение "вар" забавно. не используйте "var".

Fattie 16.08.2020 20:39

У чрезмерного использования var есть свои минусы: он помогает только вам, а не вашей команде. Это приводит к ошибкам. Никто не хочет навести указатель мыши на ваши «вары», чтобы увидеть, что вы, возможно, имели в виду. В контексте отладки возникает мысль, что вы что-то неправильно написали. Это тот тип, который имел в виду кодировщик? Не всегда можно сказать с помощью var. Ваш код должен сообщать, что вы имеете в виду, и расширять возможности вас и вашей команды. Если вы не знаете, что делаете, - включая моменты, когда вы думаете, что знаете, что делаете, - не используйте var. Код должен быть разборчивым и простым, но не слишком простым. И уважайте свою команду!

Xonatron 09.11.2020 15:32

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

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

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

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

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 является необязательной функцией C# 3.0 и выше, вы также можете использовать здесь точный тип ваших ключей / значений)

функция var наиболее необходима для вашего первого блока кода :)

nawfal 01.11.2013 18:26

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

Rotsiser Mho 13.01.2015 09:17

Мне не нравится использование здесь var. Учитывая, что это просто синтаксический сахар, зачем его здесь использовать? Когда кто-то пытается прочитать код, ему придется «прыгать» по коду, чтобы определить тип myDictionary (если, конечно, это не настоящее имя). Я думаю, что использование var хорошо, когда тип очевиден, например. var x = "some string", но когда это не сразу очевидно, я думаю, что это ленивое кодирование, которое вредит читателю / обозревателю кода

James Wierzba 25.01.2017 20:23

На мой взгляд, var следует использовать экономно. В частности, здесь это неконструктивно: тип KeyValuePair, скорее всего, имеет отношение к вопросу.

Sinjai 21.08.2017 19:21

var имеет уникальную цель, и я не верю, что это «синтаксический» сахар. Целенаправленное использование - подходящий подход.

Joshua K 07.09.2018 21:43

использование var в операторе foreach опасно, потому что иногда компилятор заменяет «var» на «object» вместо использования правильного типа.

Maxter 03.12.2019 19:56

@JamesWierzba Я бы сказал, что ухудшает читабельность отсутствие хорошего имени переменной (из-за того, что это демонстрационный пример). В реальном коде, будь это foreach (var vehicle in line.Values) { start(vehicle); }, он был бы хорошо читаем.

spectras 11.06.2020 14:53

Что означает «foo» в большинстве примеров, существующих в StackOverFlow? Это похоже на соглашение между программистами называть свой определенный тип "foo"? (Я новичок)

Roksana 12.08.2020 08:29

@Roksana en.wikipedia.org/wiki/Foobar

Pang 07.12.2020 09:53

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

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

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

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

Поддерживая это ... и цените НЕ использование слова "var". Ненавижу образцы кода с "var" в них. Если это не "var emp = new Employee ()" ... никто не имеет / мало понимает, что это за var. Спасибо.

granadaCoder 24.07.2020 18:05

@granadaCoder, если никто не может сказать, что такое var, значит, вы неправильно называете имя

Eonasdan 14.09.2020 17:21

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

Из описания класса 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);
}

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

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

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

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

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

Это похоже на использование не-универсальной версии Dictionary ... то есть до .NET framework 2.0.

joedotnot 26.04.2014 17:21

@ joed0tnot: это не-универсальная версия, используемая для объектов Hashtable

sɐunıɔןɐqɐp 01.08.2018 22:42

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

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

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

К вашему сведению, foreach не работает, если значение имеет тип объекта.

Уточните: foreach не будет работать, если значение который имеет тип object? В противном случае в этом нет особого смысла.

Marc L. 29.12.2017 19:22

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

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

Чтобы использовать метод '.ElementAt', помните: using System.Linq; Это не входит в fx. автоматически сгенерированные тестовые классы.

Tinia 24.11.2011 18:47

Это правильный путь, если вы изменяете значения, связанные с ключами. В противном случае возникает исключение при изменении и использовании foreach ().

Mike de Klerk 03.04.2013 11:18

Это полезно, когда вы хотите изменить во время итерации. Версия foreach выдаст исключение, пока вы перечисляете коллекцию.

zionpi 21.01.2015 12:03

Разве ElementAt не является операцией O (n)?

Arturo Torres Sánchez 07.02.2015 04:46

Этот ответ совершенно не заслуживает стольких голосов. Словарь не имеет неявного порядка, поэтому использование .ElementAt в этом контексте может привести к незначительным ошибкам. Гораздо серьезнее высказанная выше точка зрения Артуро. Вы будете повторять словарь dictionary.Count + 1 раз, что приведет к сложности O (n ^ 2) для операции, которая должна быть только O (n). Если вам действительно нужен индекс (в противном случае вы, вероятно, изначально используете неправильный тип коллекции), вам следует вместо этого выполнить итерацию dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value}), а не использовать .ElementAt внутри цикла.

spender 02.03.2015 05:17

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

Ian Ringrose 24.09.2015 17:46

@MikedeKlerk, изменение словаря при его повторении небезопасно. См. Комментарии под stackoverflow.com/a/31112746/24315.

Neil 09.02.2016 03:03

ElementAt - o (n) операция! Серьезно? Это пример того, как нельзя этого делать. Это много голосов?

Mukesh Adhvaryu 13.05.2016 01:14

@spender Согласен. Конечно, альтернативой является var dictCopy = dictionary.ToArray(); (опять же с использованием расширения Linq), и впоследствии вы можете получить доступ к dictCopy по индексу массива.

Jeppe Stig Nielsen 05.06.2016 18:21

Сложность O (n ^ 2) сделает это медленным для больших коллекций.

Padmika 07.10.2016 02:20

В мой ответ to Цикл для разницы по сравнению с foreach при повторении словаря с # я сравнил несколько методов итерации Dictionary<>. Я не только обнаружил, что foreach / GetEnumerator() - самый быстрый способ сделать это, но и обнаружил, что ElementAt() - ужасный способ сделать это. Для 10-элементного словаря ElementAt() - это ~ 8x медленнее, а для 1000 элементов - ~ 400x медленнее. Среди других предложений рассмотрите возможность простого объявления индексной переменной вне цикла foreach.

Lance U. Matthews 30.04.2020 04:58

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

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

+1 для повышения производительности. Действительно, итерация по самому словарю включает некоторые накладные расходы, если все, что вам нужно, - это значения. В основном это связано с копированием значений или ссылок в структуры KeyValuePair.

Jaanus Varus 05.08.2015 13:25

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

Crescent Fresh 13.07.2018 17:15

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

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

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

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

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

Вы должны измерять что-то вроде StopWatch вместо DateTime: hanselman.com/blog/…

Even Mien 23.02.2015 22:26

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

WiiMaxx 29.07.2015 11:57

Интересно, что вы получите разные результаты в зависимости от того, какие данные у вас есть в словаре. При обходе словаря функция Enumerator должна пропускать много пустых слотов в словаре, поэтому он работает медленнее, чем итерация по массиву. Если Словарь заполнен, будет меньше пустых слотов для пропуска, чем если бы он был наполовину пуст.

Martin Brown 30.06.2016 11:36

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

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

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

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

В этом ответе должно быть больше обоснований / описаний. Что AggregateObject добавляет к KeyValuePair? Где «итерация», как указано в вопросе?

Marc L. 29.12.2017 19:37

Select выполняет итерацию по словарю и позволяет нам работать с каждым объектом. Он не такой общий, как foreach, но я его часто использовал. Действительно ли мой ответ заслуживает отрицательной оценки?

Egor Okhterov 02.06.2018 21:37

Нет, Selectиспользует итерация, чтобы повлиять на результат, но не сам итератор. Типы вещей, для которых используется итерация (foreach) - особенно операции с побочными эффектами - выходят за рамки Linq, включая Select. Лямбда не будет выполняться до тех пор, пока не будет фактически перечислен aggregateObjectCollection. Если этот ответ воспринимается как «первый путь» (т.е. используется перед прямым foreach), он поощряет плохие методы. Ситуативно могут быть операции Linq, которые могут быть полезны до для итерации словаря, но не решают заданный вопрос.

Marc L. 04.06.2018 19:11

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

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

@WiiMaxx и что более важно, если эти элементы НЕ зависят друг от друга

Mafii 23.09.2016 13:07

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

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

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

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

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)
}

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

Последним должен быть .Values, а не пункт выбора.

Mafii 23.09.2016 13:05

@Mafii Ты уверен? Значения, возвращаемые OrderBy, не относятся к типу KeyValuePair, у них нет поля Value. Я вижу здесь именно IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Возможно, вы имели в виду другое? Можете ли вы написать полную строку, показывающую, что вы имеете в виду (и протестировать это)?

Stéphane Gourichon 23.09.2016 17:30

Я думаю, что этот ответ содержит то, что я имею в виду: stackoverflow.com/a/141105/5962841, но поправьте меня, если я что-то напутал

Mafii 23.09.2016 17:34

Нет, прочтите в конце. Существует свойство .Keys и .Values. Нет необходимости запрашивать с помощью select ...

Mafii 23.09.2016 18:07

@Mafii Перечитайте весь мой ответ, пояснения между частями кода рассказывают контекст. Ответ, который вы упомянули, похож на второй раздел кода в моем ответе (порядок не требуется). Там я просто написал items.Value, как вы предложили. В случае четвертого раздела, который вы прокомментировали, Select() - это способ заставить foreach перечислять непосредственно значения в словаре, а не пары ключ-значение. Если вам почему-то не нравится Select() в этом случае, вы можете предпочесть третью часть кода. Цель четвертого раздела - показать, что можно предварительно обработать коллекцию с помощью LINQ.

Stéphane Gourichon 23.09.2016 18:14

А как насчет .Keys.Orderby ...?

Mafii 23.09.2016 18:18

Если вы сделаете .Keys.Orderby(), вы будете перебирать список ключей. Если это все, что тебе нужно, хорошо. Если вам нужны значения, то в цикле вам нужно будет запросить словарь по каждому ключу, чтобы получить значение. Во многих сценариях это не имеет практического значения. В сценарии с высокой производительностью это будет. Как я уже писал в начале ответа: «Есть много способов (...) и лучшего пути нет. Это зависит от потребности, а часто и от вкуса».

Stéphane Gourichon 23.09.2016 18:23

Ну, вы также можете .Values.Order By. только мои 5 центов;) я согласен, это не имеет особого значения.

Mafii 23.09.2016 18:24

Позвольте нам продолжить обсуждение в чате.

Stéphane Gourichon 23.09.2016 18:32

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

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

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

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 против someList.Foreach () {}), особенно при работе с большими словарями и параллельной работе это не вариант / не будет иметь никакого эффекта.

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

Если вы просто хотите «прочитать» ключи и значения, вы также можете использовать IEnumerable.Select ().

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

Копирование всей коллекции без всякой причины улучшит производительность нет. Это значительно замедлит код, а также удвоит объем памяти кода, который практически не должен потреблять дополнительную память.

Servy 16.09.2016 19:08

Я избегаю использования метода List.ForEach с побочным эффектом: foreach увеличивает видимость побочного эффекта там, где он принадлежит.

user2864740 29.03.2017 01:30

К сожалению, вы связались с вопросом, а не с конкретным ответом. На этот вопрос есть 14 ответов, некоторые из них длинные. Можете ли вы дать ссылку на точные ответы, подтверждающие ваше утверждение о том, что .ForEach() может улучшить производительность по сравнению с foreach? А еще лучше процитировать или описать соответствующие части ответа, потому что даже ссылки на переполнение стека могут быть повреждены (т. Е. Удаленный ответ).

Lance U. Matthews 10.02.2021 02:15

Простейшая форма перебора словаря:

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

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

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

Чтобы этот код работал на более низких версиях C#, добавьте 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;
    }
}

Это неверно. .NET 4.7 просто имеет встроенный ValueTuple. Он доступен как пакет nuget для более ранних версий. Что еще более важно, C# 7.0+ необходим для того, чтобы метод Deconstruct работал как деконструктор для var (fruit, number) in fruits.

David Arno 18.10.2017 14:58

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

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);
}

Используя C# 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 со строгой типизацией, что позволяет использовать этот новый удобный синтаксис.

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

Вызов метода расширения для создания tuple требует лишь незначительных затрат по сравнению с использованием KeyValuePair напрямую, что НЕ должно быть проблемой, если вы все равно назначаете свойства KeyValuePairKey и Value новым переменным цикла.

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

Проверьте это: Блог MSDN - Новые возможности C# 7

В чем причина предпочесть «удобные» кортежи парам «ключ-значение»? Я не вижу здесь выгоды. Ваш кортеж содержит ключ и значение, так же как и пара ключ-значение.

Maarten 10.09.2018 15:57

Привет, Мартен, спасибо за вопрос. Основное преимущество - читаемость кода без дополнительных усилий по программированию. С KeyValuePair всегда нужно использовать форму kvp.Key и kvp.Value для использования соответственно ключа и значения. С кортежами вы получаете гибкость, позволяя называть ключ и значение по своему усмотрению, без использования дополнительных объявлений переменных внутри блока foreach. Например. вы можете назвать свой ключ factoryName, а значение models, что особенно полезно, когда вы получаете вложенные циклы (словари словарей): обслуживание кода становится намного проще. Просто попробуйте! ;-)

sɐunıɔןɐqɐp 10.09.2018 16:49

Я написал расширение для перебора словаря.

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));

Вы определили метод ForEach, в котором у вас есть foreach (...) { } ... Кажется ненужным.

Maarten 26.03.2019 16:51

Начиная с C# 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}");
}

Цикл for будет работать даже без метода расширения Deconstruct

Vimal CK 23.09.2020 15:35

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

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
}

Создание и копирование второго словаря не является допустимым решением для читабельности кода. На самом деле, я бы сказал, что это затруднит понимание кода, потому что теперь вы должны спросить себя: «Почему последний парень создал второй словарь?» Если вы хотите быть более подробным, просто используйте первый вариант.

Matthew Goulart 06.09.2018 22:23

Я просто хотел показать вам, что когда decl dict before для каждого, использование в foreach ясно из объявления

BigChief 08.09.2018 12:03

C# 7.0 представил Деконструкторы, и если вы используете приложение .NET Core 2.0+, структура 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}");
}

Если вы используете .NET Framework, в котором по крайней мере до 4.7.2 нет Deconstruct на KeyValuePair, попробуйте следующее: foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))

nwsmith 26.04.2019 14:58

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

    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););

foreach самый быстрый, и если вы перебираете только ___.Values, он также быстрее

Почему вы звоните в ContainsKey() в версии for? Это добавляет дополнительные накладные расходы, которых нет в коде, с которым вы сравниваете. TryGetValue() существует, чтобы заменить тот точный шаблон «если ключ существует, получить элемент с ключом». Кроме того, если dict содержит непрерывный диапазон целых чисел от 0 до dictCount - 1, вы знаете, что индексатор не может дать сбой; в противном случае вам следует повторять dict.Keys. В любом случае, ContainsKey() / TryGetValue() не требуется. Наконец, пожалуйста, не публикуйте скриншоты кода.

Lance U. Matthews 29.04.2020 21:55

Если вы хотите использовать цикл for, вы можете сделать это:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }

Но какая в этом польза? Это более длинный код, чем цикл foreach. и - худшая производительность, потому что new List<string>(dictionary.Keys) будет повторять dictionary.Count раз, прежде чем вы даже сможете повторить его самостоятельно. Не говоря уже о том, что вопрос о «наилучшем способе» является субъективным, я не понимаю, как это можно квалифицировать как «лучший способ» или «стандартный способ», о котором идет речь. На «Если вы хотите использовать цикл for ...» я бы противопоставил «Не надо использовать цикл for».

Lance U. Matthews 15.05.2020 20:58

Если у вас большая коллекция и операции в foreach выполняются медленно, и если ваша коллекция может измениться при итерации, это защитит вас от ошибки «коллекция изменена», и это решение имеет лучшую производительность, чем при использовании ElementAt.

Seçkin Durgay 16.05.2020 15:01

Я согласен с тем, что избегание исключений «Коллекция была изменена» является одной из причин для этого, хотя этот особый случай не был указан в вопросе, и всегда можно было сделать foreach (var pair in dictionary.ToArray()) { }. Тем не менее, я думаю, что было бы хорошо указать в ответе конкретный сценарий (-ы), в котором можно было бы использовать этот код, и последствия этого.

Lance U. Matthews 16.05.2020 22:26

да, вы правы, но здесь есть ответ с помощью ElementAt, и он имеет очень высокую репутацию, и я ввел этот ответ :)

Seçkin Durgay 16.05.2020 23:12

Как уже указывалось в этом отвечать, KeyValuePair<TKey, TValue> реализует метод Deconstruct, начиная с .NET Core 2.0, .NET Standard 2.1 и .NET Framework 5.0 (предварительная версия).

Благодаря этому можно выполнять итерацию по словарю независимо от KeyValuePair:

var dictionary = new Dictionary<int, string>();

// ...

foreach (var (key, value) in dictionary)
{
    // ...
}

Лучший ответ, конечно же: Не используйте точный словарь, если вы планируете перебирать его.- как уже упоминал Викас Гупта в обсуждении под вопросом. Но в этом обсуждении, как и во всей этой ветке, все еще нет на удивление хороших альтернатив. Один:

SortedList<string, string> x = new SortedList<string, string>();

x.Add("key1", "value1");
x.Add("key2", "value2");
x["key3"] = "value3";
foreach( KeyValuePair<string, string> kvPair in x )
            Console.WriteLine($"{kvPair.Key}, {kvPair.Value}");

Почему многие люди считают это запахом кода перебора словаря (например, с помощью foreach (KeyValuePair <,>): Это довольно удивительно, но есть мало цитируемый, но основной принцип чистого кодирования: «Выразите намерение!» Роберт С. Мартин пишет в «Чистом коде»: «Выбор имен, раскрывающих намерение». Очевидно, это слишком слабо. "Выражать (раскрывать) намерение при каждом решении кодирования " было бы лучше. Моя формулировка. Мне не хватает хорошего первого источника, но я уверен, что есть ... Связанный принцип - "Принцип наименьшего удивления" (= Принцип наименьшего удивления).

Почему это связано с перебором словаря? Выбор словаря выражает намерение выбрать структуру данных, которая была создана для поиска данных по ключу.. В настоящее время в .NET существует так много альтернатив, что если вы хотите перебирать пары ключ / значение, этот костыль вам не понадобится.

Более того: если вы что-то повторяете, вы должны раскрыть что-то о том, как элементы (будут) заказаны и, как ожидается, будут заказаны! AFAIK, Dictionary не имеет спецификации о порядке (только соглашения, зависящие от реализации). Какие есть альтернативы?

TL; DR:
SortedList: если ваша коллекция не становится слишком большой, простым решением было бы использовать SortedList <,>, который также дает вам полную индексацию пар ключ / значение.

У Microsoft есть длинная статья об упоминании и объяснении подходящих коллекций:
Коллекция ключей

Отметим самые важные: KeyedCollection <,> и SortedDictionary <,>. SortedDictionary <,> немного быстрее, чем SortedList для простой вставки, если он становится большим, но не имеет индексации и требуется только в том случае, если O (log n) для вставки предпочтительнее других операций. Если вам действительно нужен O (1) для вставки и примите более медленные итерации взамен, вы должны использовать простой Dictionary <,>. Очевидно, что не существует самой быстрой структуры данных для всех возможных операций.

Дополнительно есть ImmutableSortedDictionary <,>.

И если одна структура данных не совсем то, что вам нужно, тогда производите от Dictionary <,> или даже из нового ConcurrentDictionary <,> и добавьте явные функции итерации / сортировки!

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