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

Ответ

Коллизия хэшей (одинаковый хэш у разных объектов) — нормальная ситуация, которую хэш-таблицы (как 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) в худшем случае, так как поиск внутри корзины становится линейным.

Ответ 18+ 🔞

А, ну тут про коллизии хэшей рассказывают, как будто это какая-то страшная тайна! Да это же, блядь, обычное дело, как пробка в сортире после праздников.

Смотри, есть у тебя, например, Dictionary или Set. Они внутри себя всё по корзинкам раскладывают, чтобы быстро искать. Хэш-функция — это как сортировщик на почте: смотрит на объект и говорит — «а, этот в корзину номер 5». Но иногда, сука, случается так, что два разных объекта получают один и тот же номер корзины. Это и есть коллизия, ёпта! Не ошибка, а штатная ситуация, как очередь в кассу в пятницу вечером.

Как это всё работает, если по-простому:

  1. Берёшь объект, считаешь его хэш — получаешь номер корзины.
  2. Заглядываешь в корзину, а там уже кто-то сидит! Вот тебе и коллизия, блядь.
  3. Swift не паникует. Он просто запихивает обоих в одну корзину, как селёдок в бочку. Использует либо цепочки, либо открытую адресацию — но суть одна: оба там помещаются.
  4. А чтобы потом не перепутать, кто есть кто, он вызывает проверку на равенство (==). Вот тут-то и выясняется, что один объект — это «Swift», а другой — «Kotlin», и их можно отличить, даже если хэш у них совпал.

Вот, смотри, наглядный пиздёж:

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). Представь, что почтальон вместо того, чтобы взять конверт из нужной ячейки, вываливает на стол содержимое целой корзины и начинает там копаться. Эффективность, блядь, на нуле!

Так что коллизии — это не конец света, а просто повод не херачить хэш-функцию как попало. А то получится, как в том анекдоте: «Все ключи — в одну корзину, и ищи там, свинья, свой!».