Ответ
Set определяет уникальность элемента, используя протокол Hashable, который должен быть реализован типом его элементов. Проверка выполняется в два этапа:
- Хеширование: Вычисляется целочисленный
hashValue(или используетсяhash(into:)) для элемента. Это быстрое приблизительное сравнение. Элементы с разными хешами гарантированно разные. - Проверка на равенство: Если хеши совпали (произошла коллизия),
Setвызывает оператор==(из протоколаEquatable, который является частьюHashable), чтобы точно определить, равны ли элементы.
Элемент считается дубликатом и не добавляется в Set, только если оба условия верны: его hashValue равен хешу существующего элемента И оператор == возвращает true.
Пример с кастомным типом:
struct User: Hashable {
let id: Int
let email: String
// Компилятор может синтезировать реализацию Hashable и Equatable,
// если все свойства также соответствуют Hashable.
// В данном случае он сравнит и id, и email.
}
let user1 = User(id: 1, email: "alice@example.com")
let user2 = User(id: 1, email: "alice@example.com")
let user3 = User(id: 1, email: "bob@example.com") // email другой
var userSet: Set<User> = [user1]
userSet.insert(user2) // Не добавится: hash и == совпадают с user1
userSet.insert(user3) // Добавится: hash может совпасть (коллизия по id),
// но == вернет false (email разный), значит, это другой элемент.
Критически важное правило: Реализации hash(into:) и == должны быть согласованы. Два экземпляра, которые равны по ==, обязаны иметь одинаковый хеш. Обратное не обязательно (коллизии допустимы).