Я видел несколько разных способов перебора словаря на С#. Есть ли стандартный способ?
foreach(KeyValuePair<string, string> entry in myDic)
{
// do something with entry.Value or entry.Key
}
var entry
, и поэтому я проголосовал за этот ответ по второму взгляду вместо вышеупомянутого.
var
когда вы не знаете тип, обычно плохая практика.
Если вы пытаетесь использовать общий словарь в С#, как если бы вы использовали ассоциативный массив на другом языке:
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 и выше, вы также можете использовать точный тип своих ключей/значений здесь)
В некоторых случаях вам может понадобиться счетчик, который может быть предоставлен для реализации цикла. Для этого LINQ предоставляет ElementAt
, который позволяет следующее:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
В зависимости от того, находитесь ли вы за клавишами или значениями...
Из MSDN Словарь < (Of < (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);
}
Как правило, запрос "наилучшего пути" без конкретного контекста - это вопрос о том, какой лучший цвет.
С одной стороны, есть много цветов и нет лучшего цвета. Это зависит от необходимости и часто от вкуса.
С другой стороны, существует множество способов перебора словаря в С#, и нет лучшего способа. Это зависит от необходимости и часто от вкуса.
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
а не предложение select.
Value
. Точный тип, который я вижу здесь, это IOrderedEnumerable<KeyValuePair<TKey, TValue>>
. Возможно, вы имели в виду что-то еще? Можете ли вы написать полную строку, показывающую, что вы имеете в виду (и проверить это)?
Я бы сказал, что foreach - стандартный способ, хотя он, очевидно, зависит от того, что вы ищете
foreach(var value in my_dictionary) {
...
}
Это то, что вы ищете?
kvp
обычно используется для именования экземпляров foreach(var kvp in myDictionary){...
словарей и связанных структур данных: foreach(var kvp in myDictionary){...
Есть много вариантов. Мой личный фаворит - KeyValuePair
Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
Foreach (KeyValuePair<string,object> kvp in myDictionary)
{
//Do some interesting things;
}
Вы также можете использовать Коллекции клавиш и ценностей
Вы также можете попробовать это на больших словарях для многопоточной обработки.
dictionary
.AsParallel()
.ForAll(pair =>
{
// Process pair.Key and pair.Value here
});
Я понимаю, что у этого вопроса уже было много ответов, но я хотел сделать небольшое исследование.
Итерация по словарю может быть довольно медленной по сравнению с итерацией над чем-то вроде массива. В моих тестах итерация по массиву заняла 0.015003 секунды, тогда как итерация по словарю (с таким же количеством элементов) заняла 0.0365073 секунды, что в 2,4 раза длиннее! Хотя я видел гораздо большие различия. Для сравнения, List был где-то посередине между 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 секунды. Разумно ожидать, что тип данных повлияет на то, насколько медленнее словарь.
Каковы мои выводы?
Вы предложили выполнить итерацию
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
не работает, если значение имеет объект типа.
foreach
не будет работать, если какое значение имеет тип object
? В противном случае это не имеет особого смысла.
Простейшая форма для итерации словаря:
foreach(var item in myDictionary)
{
Console.WriteLine(item.Key);
Console.WriteLine(item.Value);
}
Иногда, если вам нужны только значения, которые нужно перечислить, используйте набор значений словаря:
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
Я нашел этот метод в документации для класса DictionaryBase в MSDN:
foreach (DictionaryEntry de in myDictionary)
{
//Do some stuff with de.Value or de.Key
}
Это был единственный, с которым я смог нормально функционировать в классе, унаследованном от DictionaryBase.
Hashtable
Стандартный способ перебора словаря в соответствии с официальной документацией на MSDN:
foreach (DictionaryEntry entry in myDictionary)
{
//Read entry.Key and entry.Value here
}
Я воспользуюсь преимуществом .NET 4.0+ и предоставил обновленный ответ на первоначально принятый вариант:
foreach(var entry in MyDic)
{
// do something with entry.Value or entry.Key
}
Если, скажем, вы хотите перебирать коллекцию значений по умолчанию, я считаю, что вы можете реализовать IEnumerable < > , где T - тип объекта значений в словаре, а "this" - словарь.
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
Просто хотел добавить мой 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 } );
foreach
побочными эффектами: foreach
видимость побочных эффектов там, где они есть.
С .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;
}
}
ValueTuple
. Он доступен как пакет nuget для более ранних версий. Что еще более важно, C # 7.0+ необходим, чтобы метод Deconstruct
работал в качестве деконструктора для var (fruit, number) in fruits
.
Не стандартный способ, но работающий:
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();
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
AggregateObject
добавляет в KeyValuePair
? Где находится «итерация», как было указано в вопросе?
foreach
, но я часто его использовал. Действительно ли мой ответ заслуживает отрицательного ответа?