Здравствуйте, друзья! Сегодня мы разберем одну из основ работы с коллекциями в
Swift: тип данных
Set и протокол
Hashable. Это критически важно для понимания, как
Swift управляет уникальностью объектов.
Set это неупорядоченная коллекция, которая хранит только уникальные элементы. Но как
Swift определяет, что объект уникальный? Всё основано на двух столпах: хэш-значении и операции сравнения.
Что происходит под капотом при добавления в Set:
Когда мы пытаемся добавить новый элемент,
Swift проделывает два шага:
- Вычисляет hashValue объекта. Это своего рода цифровой «отпечаток».
- Сравнивает этот отпечаток с существующими с помощью оператора == (равенства).
Если хэш уникальный, объект считается новым и добавляется в коллекцию. Если же хэш совпал с одним из уже имеющихся,
Swift делает дополнительную проверку через
==, чтобы убедиться, что это не просто коллизия, а действительно один и тот же элемент.
Пример:
Допустим, у нас есть структура
Room (Комната) с координатами на карте:
struct Room {
let position: (x: Int, y: Int)
}
Чтобы добавить такие комнаты в
Set, мы должны подписать структуру под протокол
Hashable.
extension Room: Hashable {
// Проверка на равенство
static func == (lhs: Room, rhs: Room) -> Bool {
// Две комнаты равны, если их координаты идентичны
return lhs.position.x == rhs.position.x && lhs.position.y == rhs.position.y
}
// Генерация хэша
func hash(into hasher: inout Hasher) {
hasher.combine(position.x)
hasher.combine(position.y)
}
}
Что такое хеш-коллизия и почему она страшна:
Коллизия хеша это ситуация, когда два разных объекта имеют одинаковый хеш.
Чем плохи коллизии:
- Производительность: каждая коллизия заставляет Swift делать дополнительную проверку через ==, что замедляет работу с коллекцией.
- Логические ошибки: плохой хеш-алгоритм может создать множество коллизий, превращая Set из быстрой структуры данных в медленную.
Почему это так важно:
- Метод == позволяет задать логику проверки равенства пользовательских типов.
- Метод hash(into:) создает тот самый «отпечаток».
Мы «смешиваем» в хэшере оба значимых поля: x и y. Почему не только x? Потому что это привело бы к коллизиям: комнаты с координатами (1, 1) и (1, 2) имели бы одинаковый хэш, и одна из них не попала бы в Set, хотя они разные!
Вывод:
Всегда, когда вы реализуете
Hashable, ваша задача: гарантировать, что если два объекта равны (== возвращает true), то и их хэш-значения должны быть идентичными. Обратное утверждение (если хэши равны, то и объекты равны) не всегда верно из-за возможных коллизий, но хороший хэш-алгоритм сводит их к минимуму.
Используйте
combine для всех значимых свойств, которые участвуют в проверке на равенство. Это залог корректной и эффективной работы ваших коллекций.