Как правильно перебрать (итерировать) все элементы Dictionary в C#?
Основной способ: цикл foreach по KeyValuePair
Самый правильный и производительный способ перебора всех элементов словаря в C# — использование цикла foreach по коллекции KeyValuePair. Каждый элемент словаря представлен структурой KeyValuePair<TKey, TValue>, которая содержит свойства Key и Value.
Dictionary<string, int> students = new Dictionary<string, int>
{
{ "Анна", 85 },
{ "Иван", 92 },
{ "Мария", 78 }
};
// Перебор всех пар ключ-значение
foreach (KeyValuePair<string, int> student in students)
{
Console.WriteLine($"Студент: {student.Key}, Оценка: {student.Value}");
}
Если вы используете C# 7.0 или новее, можно упростить код с помощью деконструкции кортежей:
foreach (var (name, grade) in students)
{
Console.WriteLine($"Студент: {name}, Оценка: {grade}");
}
Альтернатива: перебор ключей или значений отдельно
Иногда нужно пройтись только по ключам или только по значениям. Для этого у Dictionary есть свойства Keys и Values:
// Только ключи
foreach (string name in students.Keys)
{
Console.WriteLine($"Студент: {name}");
}
// Только значения
foreach (int grade in students.Values)
{
Console.WriteLine($"Оценка: {grade}");
}
Почему не работает for по индексу?
Dictionary — это хеш-таблица, а не список с последовательными индексами. Элементы хранятся в произвольном порядке (в зависимости от хеш-функции). Хотя технически можно использовать ElementAt(index) из LINQ, это крайне неэффективно, так как при каждом обращении словарь будет заново искать элемент по индексу. Никогда так не делайте в production-коде:
// ПЛОХОЙ пример — медленно и нечитаемо
for (int i = 0; i < students.Count; i++)
{
var pair = students.ElementAt(i); // O(n) на каждой итерации!
Console.WriteLine($"{pair.Key}: {pair.Value}");
}
Важные замечания
- Порядок элементов: Dictionary не гарантирует порядок хранения. Если нужен упорядоченный словарь, используйте
SortedDictionary(сортировка по ключу) илиOrderedDictionary(сохранение порядка вставки). - Изменение словаря во время перебора: Не пытайтесь добавлять или удалять элементы внутри цикла
foreach— это вызовет исключениеInvalidOperationException. Если нужно модифицировать словарь, сначала соберите ключи в отдельный список. - Производительность:
foreachпо словарю работает за O(n) и является оптимальным способом полного перебора.
Таким образом, запомните золотое правило: для перебора Dictionary используйте foreach (var pair in dictionary) — это просто, быстро и понятно.
Использование LINQ для более сложных сценариев
Если вам нужно не просто вывести все элементы, а выполнить фильтрацию, проекцию или агрегацию, удобно использовать LINQ-методы в связке с Dictionary.
Например, найдём всех студентов с оценкой выше 80:
Dictionary<string, int> students = new Dictionary<string, int>
{
{ "Анна", 85 },
{ "Иван", 92 },
{ "Мария", 78 }
};
// Фильтрация по значению
var excellentStudents = students
.Where(pair => pair.Value > 80)
.Select(pair => pair.Key);
Console.WriteLine("Отличники: " + string.Join(", ", excellentStudents));
// Вывод: Отличники: Анна, Иван
Также можно преобразовать словарь в другой тип данных:
// Создаём список кортежей (имя, оценка, статус)
var studentReports = students
.Select(pair => (
Name: pair.Key,
Grade: pair.Value,
Status: pair.Value >= 80 ? "Сдал" : "Не сдал"
))
.ToList();
Обратите внимание: LINQ-запросы возвращают новую коллекцию и не изменяют исходный словарь. Если вам нужно часто выполнять такие операции, возможно, стоит рассмотреть использование ConcurrentDictionary для многопоточных сценариев или специализированные структуры данных.
Главное преимущество LINQ — читаемость и гибкость, но помните о небольшом накладном расходе на создание итераторов. Для простого перебора без дополнительной логики лучше использовать обычный foreach.