HashSet и уникальность
Отслеживайте различные значения.
«HashSet и уникальность» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Что такое HashSet
HashSet<T> хранит коллекцию уникальных значений без дубликатов. Добавление уже присутствующего значения ничего не делает.
Как и словарь, он использует хеш-таблицу, поэтому проверки наличия в среднем выполняются за O(1). Порядок добавления не сохраняется.
using System.Collections.Generic;
var seen = new HashSet<int>();
seen.Add(1);
seen.Add(1); // ignored, still one elementAdd возвращает логическое значение
Add возвращает true, если значение было новым, и false, если оно уже присутствовало.
Это возвращаемое значение позволяет за один шаг обнаруживать дубликаты без отдельной проверки Contains.
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var set = new HashSet<string>();
Console.WriteLine(set.Add("a")); // True
Console.WriteLine(set.Add("a")); // False
}
}Быстрые проверки наличия
Contains у HashSet в среднем работает за O(1), тогда как у List — за O(n).
Если вы постоянно спрашиваете: «Встречалось ли мне это значение?», множество — подходящий инструмент. По мере роста коллекции разница в скорости становится огромной.
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var ids = new HashSet<int> { 10, 20, 30 };
Console.WriteLine(ids.Contains(20)); // True
Console.WriteLine(ids.Contains(99)); // False
}
}Удаление дубликатов из списка
Распространённая задача — взять список и оставить только различные значения. Создание HashSet из списка решает её за O(n).
В получившемся множестве каждое значение встречается один раз. Если снова нужен список, оберните множество так: new List<int>(set).
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var nums = new List<int> { 1, 2, 2, 3, 3, 3 };
var unique = new HashSet<int>(nums);
Console.WriteLine(unique.Count); // 3
}
}Объединение
UnionWith добавляет каждый элемент другой коллекции, пропуская дубликаты. В результате содержатся все элементы обеих коллекций.
Множество изменяется на месте. Это гораздо чище, чем вручную перебирать элементы и вызывать для каждого Add.
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var a = new HashSet<int> { 1, 2, 3 };
a.UnionWith(new[] { 3, 4, 5 });
Console.WriteLine(a.Count); // 5
}
}Пересечение
IntersectWith оставляет только элементы, которые также встречаются в другой коллекции, удаляя остальные.
Используйте его для поиска общих элементов, например общих тегов двух статей или пользователей (users), входящих в обе группы (groups).
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var a = new HashSet<int> { 1, 2, 3, 4 };
a.IntersectWith(new[] { 2, 4, 6 });
Console.WriteLine(string.Join(",", a)); // 2,4
}
}Разность
ExceptWith удаляет из множества все элементы, найденные в другой коллекции, оставляя элементы, уникальные для этого множества.
Это операция set-minus: «элементы в A, но не в B». Она отлично подходит для вычисления изменений или определения того, что осталось обработать.
var a = new HashSet<int> { 1, 2, 3, 4 };
a.ExceptWith(new[] { 2, 4 });
// a now contains 1 and 3Подмножество и надмножество
IsSubsetOf и IsSupersetOf проверяют отношения включения между множествами и возвращают bool.
Overlaps проверяет, есть ли общий элемент, а SetEquals — содержат ли два множества ровно одни и те же элементы независимо от порядка.
var a = new HashSet<int> { 1, 2 };
var b = new HashSet<int> { 1, 2, 3 };
bool sub = a.IsSubsetOf(b); // true
bool ov = a.Overlaps(b); // trueРавенство элементов
Уникальность зависит от GetHashCode и Equals типа элемента — точно так же, как для ключей словаря.
Для пользовательских типов переопределите оба метода или используйте record. Иначе два логически равных объекта будут считаться разными, и оба попадут в множество (set).
var names = new HashSet<string>(
System.StringComparer.OrdinalIgnoreCase);
names.Add("Sam");
bool dup = !names.Add("SAM"); // true: treated as sameНет индексации и порядка
У HashSet нет доступа по индексу: нельзя написать set[0]. Его можно только перебрать с помощью foreach.
Порядок перечисления не гарантируется. Если нужны и уникальность, и отсортированный порядок (sorted), используйте SortedSet<T>, который поддерживает упорядоченность элементов ценой операций за O(log n).
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var s = new SortedSet<int> { 3, 1, 2 };
Console.WriteLine(string.Join(",", s)); // 1,2,3
}
}Отслеживание встреченных элементов
Распространённый шаблон — фильтровать поток так, чтобы каждое значение появлялось один раз. Добавляйте значение в множество (set) и действуйте только тогда, когда Add возвращает true.
Это чище и быстрее, чем проверять растущий список: сканирование за O(n²) превращается в O(n).
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var seen = new HashSet<int>();
foreach (int x in new[] { 1, 1, 2, 3, 2 })
if (seen.Add(x)) Console.Write(x + " ");
// prints 1 2 3
}
}Быстрая проверка
Вспомните, какие ограничения задаёт HashSet и какова стоимость поиска.
Итоги
HashSet<T> хранит уникальные значения и в среднем обеспечивает добавление, удаление и проверку наличия за O(1), но не поддерживает порядок или индексацию.
Он особенно полезен для устранения дубликатов, быстрых проверок «встречалось ли мне это (seen)» и операций над множествами с помощью UnionWith, IntersectWith и ExceptWith. Пользовательские типы элементов должны корректно реализовывать равенство.
Часто задаваемые вопросы
Урок «HashSet и уникальность» бесплатный?
Да — полный текст урока «HashSet и уникальность» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «HashSet и уникальность»?
Отслеживайте различные значения. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «HashSet и уникальность»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Списки на практике
- Поиск в словарях
- HashSet и уникальность
- Выбор коллекции