Множества (Set) в C# (.NET 6): подробное руководство
Множества (Set) в C#: Уникальные коллекции без повторов
Что такое множество и зачем оно нужно?
Представь, что ты собираешь коллекцию наклеек. Ты хочешь, чтобы каждая наклейка была только одна — никаких дубликатов. Именно так работает множество (Set) в программировании.
Множество — это коллекция, которая хранит только уникальные элементы. Если ты попробуешь добавить в него уже существующий элемент, он просто не добавится. Это очень удобно, когда нужно:
- Убрать повторяющиеся слова из текста
- Проверить, есть ли элемент в коллекции
- Объединить два списка без дубликатов
В C# для работы с множествами используется тип HashSet<T> (где T — это тип элементов, например, string или int).
Основной синтаксис
Создать пустое множество:
HashSet<string> продукты = new HashSet<string>();
Создать множество с начальными элементами:
HashSet<string> фрукты = new HashSet<string>() { "яблоко", "банан", "апельсин" };
Добавить элемент:
продукты.Add("молоко");
Удалить элемент:
продукты.Remove("молоко");
Проверить, есть ли элемент:
if (продукты.Contains("молоко"))
{
Console.WriteLine("Молоко есть в списке!");
}
Узнать количество элементов:
int количество = продукты.Count;
Перебрать все элементы (каждый встретится только один раз):
foreach (string продукт in продукты)
{
Console.WriteLine(продукт);
}
Главные операции с множествами
Множества поддерживают математические операции, которые очень полезны на практике:
Объединение (UnionWith) — добавляет все элементы из другого множества или списка:
HashSet<string> основные = new HashSet<string>() { "хлеб", "молоко" };
HashSet<string> дополнительные = new HashSet<string>() { "молоко", "яйца" };
основные.UnionWith(дополнительные);
// Теперь основные содержит: "хлеб", "молоко", "яйца" (молоко не продублировалось!)
Пересечение (IntersectWith) — оставляет только те элементы, которые есть в обоих множествах:
HashSet<string> моиИнгредиенты = new HashSet<string>() { "сыр", "помидор", "базилик" };
HashSet<string> рецептПиццы = new HashSet<string>() { "сыр", "колбаса", "помидор" };
моиИнгредиенты.IntersectWith(рецептПиццы);
// Теперь моиИнгредиенты содержит: "сыр", "помидор"
Разность (ExceptWith) — удаляет элементы, которые есть в другом множестве:
HashSet<string> списокПокупок = new HashSet<string>() { "хлеб", "молоко", "яйца", "масло" };
HashSet<string> ужеКупил = new HashSet<string>() { "молоко", "яйца" };
списокПокупок.ExceptWith(ужеКупил);
// Теперь списокПокупок содержит: "хлеб", "масло" (то, что ещё нужно купить)
Разбор типичных задач
Задача: Уникальные ингредиенты пиццы
Пользователь вводит ингредиенты через запятую. Нужно вывести только уникальные.
Console.WriteLine("Введите ингредиенты через запятую:");
string ввод = Console.ReadLine();
// Разделяем строку по запятым
string[] ингредиенты = ввод.Split(',');
// Создаём множество — оно автоматически уберёт дубликаты
HashSet<string> уникальные = new HashSet<string>();
foreach (string ингредиент in ингредиенты)
{
// Убираем лишние пробелы и добавляем
уникальные.Add(ингредиент.Trim());
}
Console.WriteLine("Уникальные ингредиенты:");
foreach (string ингредиент in уникальные)
{
Console.WriteLine("- " + ингредиент);
}
Задача: Объединение списков покупок для вечеринки
Console.WriteLine("Введите ваш список покупок (через запятую):");
string вашВвод = Console.ReadLine();
string[] вашСписок = вашВвод.Split(',');
Console.WriteLine("Введите список от гостей (через запятую):");
string гостиВвод = Console.ReadLine();
string[] гостиСписок = гостиВвод.Split(',');
// Создаём множество из первого списка
HashSet<string> общийСписок = new HashSet<string>();
foreach (string товар in вашСписок)
{
общийСписок.Add(товар.Trim());
}
// Добавляем товары от гостей (дубликаты не добавятся)
foreach (string товар in гостиСписок)
{
общийСписок.Add(товар.Trim());
}
Console.WriteLine("Итоговый список покупок (без повторов):");
foreach (string товар in общийСписок)
{
Console.WriteLine("- " + товар);
}
Частые ошибки новичков
Ошибка 1: Пытаться получить элемент по индексу, как в массиве.
// ❌ Так нельзя
string элемент = моёМножество[0]; // Ошибка!
// ✅ Нужно перебирать через foreach или проверять Contains
Ошибка 2: Забывать, что дубликаты не добавляются, и считать, что Add вернёт true.
HashSet<string> set = new HashSet<string>();
bool добавилось = set.Add("яблоко"); // true
bool добавилось2 = set.Add("яблоко"); // false — яблоко уже есть
// Метод Add возвращает true, если элемент действительно добавился
Ошибка 3: Не очищать строки от пробелов перед добавлением.
// Если пользователь ввёл: "сыр, помидор, сыр"
// Без Trim() получится: " сыр", " помидор", " сыр" — три разных элемента!
// С Trim(): "сыр", "помидор", "сыр" — дубликаты уберутся
Итоговый пример: Записная книжка контактов
Создадим программу, которая хранит имена контактов (только уникальные) и их номера телефонов.
// Хранилище контактов: имя → множество телефонов
Dictionary<string, HashSet<string>> записнаяКнижка = new Dictionary<string, HashSet<string>>();
while (true)
{
Console.WriteLine("\n1. Добавить контакт");
Console.WriteLine("2. Показать все контакты");
Console.WriteLine("3. Выйти");
Console.Write("Выберите действие: ");
string выбор = Console.ReadLine();
if (выбор == "1")
{
Console.Write("Введите имя контакта: ");
string имя = Console.ReadLine().Trim();
Console.Write("Введите номер телефона: ");
string телефон = Console.ReadLine().Trim();
// Если контакта ещё нет — создаём новое множество для телефонов
if (!записнаяКнижка.ContainsKey(имя))
{
записнаяКнижка[имя] = new HashSet<string>();
}
// Добавляем телефон (если такой уже есть — не добавится)
if (записнаяКнижка[имя].Add(телефон))
{
Console.WriteLine($"Телефон {телефон} добавлен для контакта {имя}");
}
else
{
Console.WriteLine($"Телефон {телефон} уже есть у контакта {имя}");
}
}
else if (выбор == "2")
{
foreach (var контакт in записнаяКнижка)
{
Console.WriteLine($"\n{контакт.Key}:");
foreach (string телефон in контакт.Value)
{
Console.WriteLine($" - {телефон}");
}
}
}
else if (выбор == "3")
{
break;
}
}
Console.WriteLine("До свидания!");
В этом примере HashSet<string> гарантирует, что у одного контакта не будет дублирующихся номеров телефонов. Попробуй добавить один и тот же номер дважды — второй раз он не добавится!
Когда использовать множества
- Нужно хранить только уникальные значения
- Нужно быстро проверить, есть ли элемент в коллекции
- Нужно объединять или сравнивать коллекции
- Порядок элементов не важен
Множества работают очень быстро — в тысячи раз быстрее, чем проверка в обычном списке, если элементов много. Это делает их идеальным выбором для задач, где важна скорость поиска.