Что происходит при коллизии хэшей у двух объектов в Swift?

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

Ответ

Коллизия хэшей (одинаковый хэш у разных объектов) — нормальная ситуация, которую хэш-таблицы (как Dictionary или Set) умеют обрабатывать.

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

  1. Хэш-функция вычисляет индекс корзины (bucket) в таблице.
  2. Если в эту корзину уже помещен другой элемент, возникает коллизия.
  3. Swift разрешает коллизии, используя цепочки или открытую адресацию, храня оба объекта в одной корзине.
  4. Для точного определения нужного объекта используется проверка равенства (==).

Пример коллизии:

struct Book: Hashable {
    let isbn: String // Уникальный идентификатор
    let title: String

    // Намеренно «плохая» хэш-функция, использующая только длину названия
    func hash(into hasher: inout Hasher) {
        hasher.combine(title.count) // У «Swift» и «Kotlin» хэш будет одинаковым (длина 5)
    }

    static func == (lhs: Book, rhs: Book) -> Bool {
        // Для равенства важен isbn
        return lhs.isbn == rhs.isbn
    }
}

let book1 = Book(isbn: "123", title: "Swift")   // Хэш для длины 5
let book2 = Book(isbn: "456", title: "Kotlin")  // Хэш для длины 5 -> КОЛЛИЗИЯ

var library = Set<Book>()
library.insert(book1)
library.insert(book2) // Оба объекта будут добавлены, так как isbn разные.

Последствия:

  • Корректность: Логика программы не нарушается, если правильно реализованы hash(into:) и ==.
  • Производительность: Частые коллизии ухудшают производительность с O(1) до O(n) в худшем случае, так как поиск внутри корзины становится линейным.