0Pricing
C# Academy · Урок

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 element

Add возвращает логическое значение

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 — локальная установка не требуется.

Все уроки этого курса

  1. Списки на практике
  2. Поиск в словарях
  3. HashSet и уникальность
  4. Выбор коллекции
← Назад к C# Academy