Как Dictionary в Swift использует хеширование и проверку равенства?

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

Ответ

Dictionary — это хеш-таблица, которая для быстрого доступа к значениям по ключу использует два механизма протокола Hashable:

  1. hash(into:) / hashValue: Преобразует ключ в целочисленный хеш, определяющий "корзину" (bucket), где будет храниться пара ключ-значение.
  2. == (Equatable): Проверяет равенство ключей. Это критически важно, так как разные ключи могут иметь одинаковый хеш (коллизия).

Последовательность операций при вставке или поиске:

let key = MyKey(...)
let hashValue = key.hashValue // 1. Вычисляем хеш для определения корзины
// 2. В найденной корзине ищем ключ, используя `==`
if existingKeyInBucket == key {
    // Нашли совпадение — обновляем значение или возвращаем его
}

Пример реализации Hashable для структуры:

struct User: Hashable {
    let id: Int // Уникальный идентификатор — идеальный кандидат для хеширования
    let name: String

    // Проверка равенства (требуется для Equatable)
    static func ==(lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id // Сравниваем только id
    }

    // Генерация хеша (требуется для Hashable)
    func hash(into hasher: inout Hasher) {
        hasher.combine(id) // В хешер добавляем только id
        // hasher.combine(name) // Добавить, если имя тоже участвует в равенстве
    }
}

// Использование
var scores: [User: Int] = [User(id: 1, name: "Alice"): 100]

Best Practices и предупреждения:

  • Хороший хеш: Должен быть быстрым и равномерно распределять ключи по корзинам.
  • Связь хеша и равенства: Два равных объекта (== возвращает true) обязаны иметь одинаковый хеш. Обратное неверно (коллизия).
  • Неизменяемые ключи: Ключ словаря должен быть неизменяемым после вставки. Изменение свойства, используемого в hash(into:) или ==, пока ключ находится в словаре, сделает словарь неработоспособным.
  • Коллизии: Swift автоматически обрабатывает коллизии (например, через цепочки внутри корзины), но их большое количество снижает производительность до O(n).