JavaRush /Курсы /C# SELF /Коллекции: OrderedDictiona...

Коллекции: OrderedDictionary и ReadOnlySet

C# SELF
34 уровень , 1 лекция
Открыта

1. Коллекция OrderedDictionary

В .NET всегда ценили богатую стандартную библиотеку, однако в жизни программиста случались ситуации с компромиссами: хочется словарь, который запоминает порядок добавления элементов, или нужно набор неизменяемых уникальных значений. До .NET 9 приходилось использовать сторонние библиотеки либо изобретать свои велосипеды (кто-то даже открывал GitHub и копировал OrderedDictionary оттуда — ш-ш-ш, мы никому не расскажем).

С выходом .NET 9 появились новые универсальные коллекции — теперь можно забыть про костыли! Давайте подробно разберём две самые полезные из них: OrderedDictionary<TKey, TValue> и ReadOnlySet<T>.

1. Что такое OrderedDictionary?

OrderedDictionary — это гибрид словаря и списка. Он хранит пары "ключ-значение", как обычный Dictionary<TKey, TValue>, но гарантирует, что порядок элементов соответствует порядку их добавления. Это особенно важно для задач, где нужно обходить элементы в том же порядке, в каком пользователь их вводил, или когда порядок влияет на бизнес-логику: печать отчётов, сериализацию данных, генерацию конфигураций.

Аналогия

Если обычный словарь — это шкаф с кучей ячеек, куда можно быстро засовывать и доставать вещи, не думая о порядке, то OrderedDictionary — это аккуратный шкафчик с выдвижными ящиками. В нём всё лежит по порядку, и вы всегда знаете, что положили сначала, а что потом.

2. Главное отличие от Dictionary

  • Dictionary: порядок элементов не гарантируется (даже если кажется, что всё получается одинаково — не верьте, это иллюзия!).
  • OrderedDictionary: элементы хранятся именно в том порядке, как были добавлены.

3. Синтаксис и основные методы

Вот базовый пример использования:


using System.Collections.Generic;

var od = new OrderedDictionary<string, int>();
od.Add("Ivan", 5);
od.Add("Svetlana", 8);
od.Add("Alex", 3);

// Перебор по порядку добавления:
foreach (var pair in od)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}

Вывод:

Ivan: 5
Svetlana: 8
Alex: 3

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

OrderedDictionary реализует те же интерфейсы, что и обычный словарь:

  • IDictionary<TKey, TValue>
  • IReadOnlyDictionary<TKey, TValue>
  • IEnumerable<KeyValuePair<TKey, TValue>>

4. Доступ по индексу и по ключу

Особенность: у OrderedDictionary есть индексатор по ключу и по порядковому индексу!


// По имени (ключу):
int svetlanaScore = od["Svetlana"]; // 8

// По индексу:
var firstEntry = od.ElementAt(0); // KeyValuePair<string, int>("Ivan", 5)

5. Обновление и удаление

Если вы добавляете новый элемент с существующим ключом, выбрасывается исключение. Если хотите заменить значение — используйте индексатор:

od["Ivan"] = 10; // Изменит существующий, порядок не изменится

Удалять можно как по ключу, так и по индексу:


od.Remove("Svetlana");
od.RemoveAt(0);      // удаляет "Ivan"

6. Визуализация структуры

flowchart LR
    A("0: Ivan - 5")
    B("1: Svetlana - 8")
    C("2: Alex - 3")
    A --> B --> C

 
Каждый узел — это пара ключ-значение, а стрелки показывают порядок добавления.

7. Подводные камни и ошибки

Многие разработчики пытаются использовать Dictionary<TKey, TValue> и надеются на порядок — но это ловушка! Даже если на одних данных порядок сохраняется, в следующий раз он изменится (особенно при изменениях .NET или на другой платформе).

В OrderedDictionary из-за хранения порядка минимально медленнее некоторые операции (вставка, удаление из середины), чем в обычном словаре, но для подавляющего большинства задач преимущества перевешивают.

Если вам нужно часто искать по индексу — это как раз про OrderedDictionary, а если нужен только быстрый поиск по ключу и неважен порядок — используйте обычный словарь.

8. Практический пример для приложения

Допустим, в нашем приложении по учёту сотрудников и отделов теперь нужен отчёт, где сотрудники выводятся в том порядке, в котором были добавлены:


var employeeScores = new OrderedDictionary<string, int>();
employeeScores.Add("Петр", 100);
employeeScores.Add("Анна", 150);
employeeScores.Add("Виктория", 80);

// Теперь отчёт всегда в нужном порядке:
foreach (var pair in employeeScores)
{
    Console.WriteLine($"{pair.Key}: {pair.Value} баллов");
}

2. Коллекция ReadOnlySet<T>

1. Что такое ReadOnlySet?

ReadOnlySet<T> — это неизменяемое (immutable) множество уникальных значений. Раньше, чтобы создать "сет" только для чтения, приходилось возвращать копию через .ToHashSet() или создавать свою обёртку. Теперь есть коллекция, в которую нельзя добавить или удалить элемент после создания. Это повышает безопасность кода и предотвращает случайные ошибки при изменении данных извне.

Аналогия

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

2. Создание ReadOnlySet

Самый простой способ создать неизменяемое множество — воспользоваться методом расширения:


var colors = new[] { "Red", "Green", "Blue", "Green" };
var readOnlyColors = colors.ToReadOnlySet();

// Теперь тут строго только уникальные значения и изменить нельзя:
foreach (var color in readOnlyColors)
    Console.WriteLine(color);

Вывод:

Red
Green
Blue

Можно использовать и прямой конструктор (если нужно):


var set = new ReadOnlySet<int>(new[] {1, 2, 2, 3, 5, 1}); // Всё равно будет 1,2,3,5

ReadOnlySet в Microsoft Docs

3. Основные свойства и методы

  • Только чтение (immutable)
  • Count — количество элементов
  • Contains(item) — проверка наличия элемента
  • Поддерживает LINQ-запросы (IEnumerable<T>)
  • Быстро ищет, но не поддерживает добавление, удаление, очистку

Пример:


if (readOnlyColors.Contains("Red"))
    Console.WriteLine("Красный есть в списке!");

Нельзя сделать так:


// Ошибка компиляции!
readOnlyColors.Add("Yellow");

4. Где это пригодится?

Очень часто вам нужно дать наружу информацию в виде множества, но не хотите, чтобы кто-то мог случайно (или злонамеренно) его изменить. Например:

  • Возвращаем перечень поддерживаемых ролей пользователя
  • Список допустимых расширений файлов
  • Перечень уникальных параметров конфигурации, которые нельзя изменять

Пример в нашем приложении: допустим, есть перечень допустимых отделов:


public static ReadOnlySet<string> Departments { get; } =
    new[] { "Кадры", "Разработка", "Бухгалтерия" }.ToReadOnlySet();

Теперь никто не подменит список отделов снаружи.

5. Визуализация: отличие от обычного HashSet

graph LR
    A[HashSet] -- Add/Remove/Contains --> B((Elements))
    C[ReadOnlySet] -- Only Contains --> D((Elements))
    
HashSet можно изменять, ReadOnlySet — никогда!

3. OrderedDictionary vs. ReadOnlySet: таблица отличий

Коллекция Хранит пары? Гарантирует порядок Можно изменять Поиск по ключу Поиск по индексу Сценарии
OrderedDictionary Да "ключ-значение" Да Да Да Да Карты, настройки, отчёты
ReadOnlySet Нет, только значения Нет Нет Да Нет Безопасные множества, константы
HashSet Нет, только значения Нет Да Да Нет Множества, где нужна модификация
Dictionary Да "ключ-значение" Нет Да Да Нет Карты без требований к порядку
2
Задача
C# SELF, 34 уровень, 1 лекция
Недоступна
Доступ по индексу и удаление элемента в OrderedDictionary
Доступ по индексу и удаление элемента в OrderedDictionary
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ