Как Swift Set определяет, что элемент уникален?

«Как Swift Set определяет, что элемент уникален?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Set определяет уникальность элемента, используя протокол Hashable, который должен быть реализован типом его элементов. Проверка выполняется в два этапа:

  1. Хеширование: Вычисляется целочисленный hashValue (или используется hash(into:)) для элемента. Это быстрое приблизительное сравнение. Элементы с разными хешами гарантированно разные.
  2. Проверка на равенство: Если хеши совпали (произошла коллизия), Set вызывает оператор == (из протокола Equatable, который является частью Hashable), чтобы точно определить, равны ли элементы.

Элемент считается дубликатом и не добавляется в Set, только если оба условия верны: его hashValue равен хешу существующего элемента И оператор == возвращает true.

Пример с кастомным типом:

struct User: Hashable {
    let id: Int
    let email: String

    // Компилятор может синтезировать реализацию Hashable и Equatable,
    // если все свойства также соответствуют Hashable.
    // В данном случае он сравнит и id, и email.
}

let user1 = User(id: 1, email: "alice@example.com")
let user2 = User(id: 1, email: "alice@example.com")
let user3 = User(id: 1, email: "bob@example.com") // email другой

var userSet: Set<User> = [user1]
userSet.insert(user2) // Не добавится: hash и == совпадают с user1
userSet.insert(user3) // Добавится: hash может совпасть (коллизия по id),
                      // но == вернет false (email разный), значит, это другой элемент.

Критически важное правило: Реализации hash(into:) и == должны быть согласованы. Два экземпляра, которые равны по ==, обязаны иметь одинаковый хеш. Обратное не обязательно (коллизии допустимы).