Каков наилучший способ перебора словаря?
-
02-07-2019 - |
Вопрос
Я видел несколько различных способов перебора словаря в C #.Есть ли стандартный способ?
Решение
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
Другие советы
Если вы пытаетесь использовать универсальный словарь в 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 и выше, вы также можете использовать точный тип ваших ключей / значений здесь)
В некоторых случаях вам может понадобиться счетчик, который может быть предоставлен реализацией цикла for.Для этого LINQ предоставляет ElementAt
который позволяет выполнить следующее:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
Зависит от того, нужны ли вам ключи или значения...
Из 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);
}
Как правило, просить "наилучший способ" без конкретного контекста - все равно что спрашивать какой самый лучший цвет?
С одной стороны, существует много цветов, и нет лучшего цвета.Это зависит от потребности, а часто и от вкуса.
С другой стороны, существует много способов перебора словаря в 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)
}
Есть еще много реальных вариантов использования, которые вы можете использовать на основе этих примеров.Если вам не нужен конкретный заказ, просто придерживайтесь "самого простого способа" (см. Выше)!
Я бы сказал, что foreach - это стандартный способ, хотя это, очевидно, зависит от того, что вы ищете
foreach(var kvp in my_dictionary) {
...
}
Это то, что ты ищешь?
Вы также можете попробовать это в больших словарях для многопоточной обработки.
dictionary
.AsParallel()
.ForAll(pair =>
{
// Process pair.Key and pair.Value here
});
Есть множество вариантов.Мой личный фаворит - от KeyValuePair
Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary)
{
// Do some interesting things
}
Вы также можете использовать Коллекции ключей и значений
Я ценю, что на этот вопрос уже было много ответов, но я хотел провести небольшое исследование.
Итерация по словарю может быть довольно медленной по сравнению с итерацией по чему-то вроде массива.В моих тестах итерация по массиву занимала 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, а версия keys - 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.
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}");
}
Ниже вы предложили повторить
Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary) {
//Do some interesting things;
}
К ТВОЕМУ сведению, foreach
не работает, если значение имеет тип object.
С .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;
}
}
Простейшая форма для перебора словаря:
foreach(var item in myDictionary)
{
Console.WriteLine(item.Key);
Console.WriteLine(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
, что позволяет вам использовать этот новый удобный синтаксис.
Он преобразует -только- необходимые словарные статьи в tuples
, поэтому он НЕ преобразует весь словарь в tuples
, так что с этим не связано никаких проблем с производительностью.
Вызов метода расширения для создания tuple
по сравнению с использованием KeyValuePair
напрямую, что НЕ должно быть проблемой, если вы назначаете KeyValuePair
свойства Key
и Value
в любом случае, к новым переменным цикла.
На практике этот новый синтаксис очень хорошо подходит для большинства случаев, за исключением низкоуровневых сценариев со сверхвысокой производительностью, где у вас все еще есть возможность просто не использовать его в этом конкретном месте.
Посмотри на это: Блог MSDN - Новые возможности в C # 7
Иногда, если вам нужны только значения для перечисления, используйте коллекцию значений словаря:
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.
Я воспользуюсь преимуществами .NET 4.0+ и предоставлю обновленный ответ на первоначально принятый:
foreach(var entry in MyDic)
{
// do something with entry.Value or entry.Key
}
Стандартным способом перебора словаря, согласно официальной документации на MSDN, является:
foreach (DictionaryEntry entry in myDictionary)
{
//Read entry.Key and entry.Value here
}
Начиная с C # 7, вы можете разбирать объекты на переменные.Я считаю, что это лучший способ перебора словаря.
Пример:
Создайте метод расширения на KeyValuePair<TKey, TVal>
это все разрушает:
public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey, out TVal val)
{
key = pair.Key;
val = 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}");
}
Если, скажем, вы хотите выполнить итерацию по коллекции значений по умолчанию, я полагаю, вы можете реализовать IEnumerable<>, где T - тип объекта values в словаре, а "this" - это словарь.
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
Просто хотел добавить свои 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 против someList.Foreach(){}), особенно при работе с большими словарями и параллельном запуске - это не вариант / вообще не будет иметь эффекта.
Также, пожалуйста, обратите внимание, что вы не сможете присваивать значения свойству 'Value' внутри цикла foreach.С другой стороны, вы также сможете манипулировать "Ключом", что, возможно, приведет к проблемам во время выполнения.
Если вы просто хотите "прочитать" ключи и значения, вы также можете использовать IEnumerable .Выберите() .
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
Я написал расширение для зацикливания на словаре.
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));
Я знаю, что это очень старый вопрос, но я создал несколько методов расширения, которые могут быть полезны:
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););
Словарь< Ключ, значение > Это универсальный класс коллекции в c #, и он хранит данные в формате значения ключа.Ключ должен быть уникальным и не может быть нулевым, тогда как значение может быть дублированным и равным нулю.Поскольку каждый элемент в словаре обрабатывается как 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);
}
в дополнение к постам с самым высоким рейтингом, где обсуждается использование
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
}
Словари представляют собой специальные списки, тогда как каждое значение в списке имеет ключ который также является переменной.Хорошим примером словаря является телефонная книга.
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's 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);