🔷 C#

Словари и хеш-таблицы в C# (.NET 6): подробное руководство

Словари и хеш-таблицы в C#

Что такое словарь и зачем он нужен?

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

Словарь (Dictionary) — это коллекция, которая хранит пары "ключ — значение". Ключ — это то, по чему мы ищем (например, имя друга), а значение — это то, что мы ищем (номер телефона).

Главное преимущество словаря — очень быстрый поиск. Неважно, хранится в словаре 10 записей или 10 000 — поиск по ключу происходит практически мгновенно. Это возможно благодаря особой структуре данных, которая называется хеш-таблица.

Как создать и использовать словарь

В C# словарь называется Dictionary, где TKey — это тип ключа, а TValue — тип значения.

// Создаём пустой словарь, где ключ — строка (имя), значение — строка (номер телефона)
Dictionary<string, string> phoneBook = new Dictionary<string, string>();

// Добавляем записи
phoneBook.Add("Аня", "+7-912-345-67-89");
phoneBook.Add("Петя", "+7-923-456-78-90");

// Получаем значение по ключу
string petePhone = phoneBook["Петя"];
Console.WriteLine($"Телефон Пети: {petePhone}");

// Проверяем, есть ли ключ в словаре
if (phoneBook.ContainsKey("Аня"))
{
    Console.WriteLine("Аня есть в справочнике!");
}

// Изменяем значение
phoneBook["Аня"] = "+7-999-888-77-66";

// Удаляем запись
phoneBook.Remove("Петя");

Другой способ создания словаря

Можно сразу создать словарь с начальными данными:

Dictionary<string, string> greetings = new Dictionary<string, string>()
{
    { "Россия", "Здравствуйте!" },
    { "Франция", "Bonjour!" },
    { "Италия", "Ciao!" },
    { "Англия", "Hello!" }
};

Console.WriteLine($"В Англии говорят: {greetings["Англия"]}");

Перебор всех элементов словаря

Чтобы пройтись по всем записям словаря, используем цикл foreach. Каждый элемент — это пара KeyValuePair, у которой есть свойства Key и Value.

Dictionary<string, int> products = new Dictionary<string, int>()
{
    { "Хлеб", 45 },
    { "Молоко", 80 },
    { "Яблоки", 120 }
};

Console.WriteLine("Наши товары:");
foreach (var item in products)
{
    Console.WriteLine($"{item.Key} стоит {item.Value} рублей");
}

Вывод программы:

Наши товары:
Хлеб стоит 45 рублей
Молоко стоит 80 рублей
Яблоки стоит 120 рублей

Словарь со списком значений

Иногда нужно, чтобы у одного ключа было несколько значений. Например, для каждого дня недели — список дел. Для этого можно использовать словарь, где значение — это список (List).

// Словарь, где ключ — день недели, значение — список дел
Dictionary<string, List<string>> weeklyPlan = new Dictionary<string, List<string>>();

// Добавляем список дел на понедельник
weeklyPlan["Понедельник"] = new List<string>() { "Сделать уроки", "Покормить кота" };

// Добавляем ещё одно дело в понедельник
weeklyPlan["Понедельник"].Add("Почитать книгу");

// Добавляем вторник
weeklyPlan["Вторник"] = new List<string>() { "Сходить в бассейн" };

// Выводим все дела на понедельник
Console.WriteLine("Дела на понедельник:");
foreach (string task in weeklyPlan["Понедельник"])
{
    Console.WriteLine($"- {task}");
}

Разбор типичных задач

Задача 1: Телефонный справочник

Нужно найти номер телефона по имени. Если имени нет — сообщить об этом.

Dictionary<string, string> phoneBook = new Dictionary<string, string>()
{
    { "Маша", "8-912-345-67-89" },
    { "Саша", "8-923-456-78-90" },
    { "Даша", "8-934-567-89-01" }
};

Console.Write("Введите имя для поиска: ");
string name = Console.ReadLine();

if (phoneBook.ContainsKey(name))
{
    Console.WriteLine($"Номер телефона {name}: {phoneBook[name]}");
}
else
{
    Console.WriteLine("Такого имени нет в справочнике");
}

Задача 2: Удаление контакта

Пользователь вводит имя, и программа удаляет этот контакт из словаря.

Dictionary<string, string> contacts = new Dictionary<string, string>()
{
    { "Иван", "8-111-222-33-44" },
    { "Мария", "8-555-666-77-88" },
    { "Алексей", "8-999-000-11-22" }
};

Console.Write("Введите имя контакта для удаления: ");
string nameToDelete = Console.ReadLine();

if (contacts.Remove(nameToDelete))
{
    Console.WriteLine($"Контакт '{nameToDelete}' удалён!");
}
else
{
    Console.WriteLine($"Контакта '{nameToDelete}' не существует");
}

// Показываем оставшиеся контакты
Console.WriteLine("Оставшиеся контакты:");
foreach (var contact in contacts)
{
    Console.WriteLine($"{contact.Key}: {contact.Value}");
}

Задача 3: Вывод всех ключей словаря

Программа выводит список всех товаров (только названия) из словаря.

Dictionary<string, int> store = new Dictionary<string, int>()
{
    { "Яблоки", 150 },
    { "Бананы", 120 },
    { "Апельсины", 180 },
    { "Виноград", 250 }
};

Console.WriteLine("Список доступных товаров:");
foreach (string productName in store.Keys)  // Keys — это коллекция всех ключей
{
    Console.WriteLine($"- {productName}");
}

Частые ошибки новичков

Ошибка 1: Обращение к несуществующему ключу

Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Вася"] = 10;

// Ошибка! Ключа "Петя" нет в словаре
// Console.WriteLine(ages["Петя"]);  // Вызовет исключение!

// Правильно: сначала проверить
if (ages.ContainsKey("Петя"))
{
    Console.WriteLine(ages["Петя"]);
}

Ошибка 2: Добавление дублирующегося ключа

Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("Ключ", "Значение 1");
// dict.Add("Ключ", "Значение 2");  // Ошибка! Ключ уже существует

// Правильно: использовать индексатор для замены значения
dict["Ключ"] = "Новое значение";  // Перезаписывает старое значение

Ошибка 3: Путаница между ключом и значением

Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "Один");
numbers.Add(2, "Два");

// Ключ — это число, значение — строка
// numbers.Add("Три", 3);  // Ошибка! Типы перепутаны

Итоговый пример: Программа для заметок

Давай напишем программу, которая объединяет все изученные приёмы. Это будет простой цифровой блокнот:

Dictionary<string, string> notebook = new Dictionary<string, string>();

Console.WriteLine("=== Цифровой блокнот ===");
Console.WriteLine("1 - Добавить контакт");
Console.WriteLine("2 - Найти контакт");
Console.WriteLine("3 - Удалить контакт");
Console.WriteLine("4 - Показать все контакты");
Console.WriteLine("5 - Выйти");

while (true)
{
    Console.Write("\nВыберите действие: ");
    string choice = Console.ReadLine();

    if (choice == "1")
    {
        Console.Write("Введите имя: ");
        string name = Console.ReadLine();
        Console.Write("Введите номер телефона: ");
        string phone = Console.ReadLine();

        if (notebook.ContainsKey(name))
        {
            Console.WriteLine("Контакт с таким именем уже существует! Обновляем номер...");
        }
        notebook[name] = phone;
        Console.WriteLine("Контакт сохранён!");
    }
    else if (choice == "2")
    {
        Console.Write("Введите имя для поиска: ");
        string name = Console.ReadLine();

        if (notebook.TryGetValue(name, out string phone))
        {
            Console.WriteLine($"Телефон {name}: {phone}");
        }
        else
        {
            Console.WriteLine("Контакт не найден");
        }
    }
    else if (choice == "3")
    {
        Console.Write("Введите имя для удаления: ");
        string name = Console.ReadLine();

        if (notebook.Remove(name))
        {
            Console.WriteLine($"Контакт '{name}' удалён!");
        }
        else
        {
            Console.WriteLine("Такого контакта нет");
        }
    }
    else if (choice == "4")
    {
        if (notebook.Count == 0)
        {
            Console.WriteLine("Блокнот пуст");
        }
        else
        {
            Console.WriteLine("Все контакты:");
            foreach (var entry in notebook)
            {
                Console.WriteLine($"{entry.Key}: {entry.Value}");
            }
        }
    }
    else if (choice == "5")
    {
        Console.WriteLine("До свидания!");
        break;
    }
    else
    {
        Console.WriteLine("Неверный выбор, попробуйте снова");
    }
}

В этом примере мы использовали:

  • Dictionary для хранения пар "имя — телефон"
  • ContainsKey для проверки существования ключа
  • TryGetValue для безопасного получения значения
  • Remove для удаления элемента
  • Count для проверки количества элементов
  • foreach для перебора всех записей

Словари — это мощный и удобный инструмент, который ты будешь использовать в своих программах очень часто. Они помогают хранить данные в понятном виде и быстро находить нужную информацию!