JavaRush /Курси /C# SELF /Колекції: OrderedDictionar...

Колекції: 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. Візуалізація структури

Кожен вузол — це пара «ключ — значення», а стрілки показують порядок додавання.

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

HashSet можна змінювати, ReadOnlySet — ніколи!

3. OrderedDictionary vs. ReadOnlySet: таблиця відмінностей

Колекція Зберігає пари? Гарантує порядок Можна змінювати Пошук за ключем Пошук за індексом Сценарії
OrderedDictionary Так, «ключ — значення» Так Так Так Так Словники, налаштування, звіти
ReadOnlySet Ні, тільки значення Ні Ні Так Ні Безпечні множини, константи
HashSet Ні, тільки значення Ні Так Так Ні Множини, яким потрібні зміни
Dictionary Так, «ключ — значення» Ні Так Так Ні Словники без вимог до порядку
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ