Как обеспечивается уникальность элементов в Set в Swift?

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

Ответ

Уникальность элементов в Set<Element> обеспечивается протоколом Hashable, который должен быть реализован для типа Element.

Механизм работы:

  1. При добавлении элемента вычисляется его хэш-значение (метод hash(into:)).
  2. Set использует это значение для быстрого поиска «корзины» (bucket), где может находиться элемент.
  3. Если в корзине уже есть элементы с таким же хэшом, проверяется равенство через оператор == (протокол Equatable, который является частью Hashable).
  4. Если элемент равен существующему, он не добавляется.

Пример с пользовательским типом:

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

    // Хэш вычисляется только по полю `id`, так как оно гарантирует уникальность
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    // Равенство также определяется по `id`
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id
    }
}

let user1 = User(id: someUUID, email: "alice@example.com")
let user2 = User(id: someUUID, email: "alice.new@example.com") // Тот же ID!

var usersSet: Set<User> = []
usersSet.insert(user1)
usersSet.insert(user2) // Не добавится, так как id совпадает
print(usersSet.count) // 1

Ключевые требования для корректной работы Set:

  • Инвариантность хэша: Хэш-значение элемента не должно меняться за время его жизни в коллекции.
  • Согласованность с равенством: Если два элемента равны (a == b), их хэши обязаны быть равными. Обратное не обязательно: разные элементы могут иметь одинаковый хэш (коллизия), но тогда Set разрешит её через проверку ==.