Как Swift обрабатывает коллизии хешей для объектов в коллекциях?

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

Ответ

Swift использует двухэтапную проверку: сначала сравнивает хеш-значения, а при их совпадении выполняет прямое сравнение объектов через оператор ==. Это гарантирует корректность даже при коллизиях.

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

  1. Вычисление хеша — объект должен соответствовать протоколу Hashable и реализовывать hash(into:).
  2. Первичная проверка — коллекция (Set, Dictionary) сравнивает хеши для быстрого поиска.
  3. Вторичная проверка — при совпадении хешей вызывается == для точного сравнения содержимого.

Пример:

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

    static func == (lhs: User, rhs: User) -> Bool {
        // Точное сравнение по уникальному полю
        return lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        // Хешируем только id, что может вызвать коллизию
        hasher.combine(id)
    }
}

let user1 = User(id: 1, email: "alice@example.com")
let user2 = User(id: 1, email: "bob@example.com")

// Хеши совпадают (оба combine(1))
// Но == вернет true, так как id одинаковы
// Объекты считаются одинаковыми, несмотря на разный email

Практические следствия:

  • Hashable должен быть согласован с Equatable: если == возвращает true, хеши должны совпадать.
  • Обратное не обязательно: совпадение хешей не гарантирует равенство объектов.
  • Коллизии лишь немного снижают производительность, но не нарушают корректность.