Ответ
Уникальность элементов в Set<Element> обеспечивается протоколом Hashable, который должен быть реализован для типа Element.
Механизм работы:
- При добавлении элемента вычисляется его хэш-значение (метод
hash(into:)). Setиспользует это значение для быстрого поиска «корзины» (bucket), где может находиться элемент.- Если в корзине уже есть элементы с таким же хэшом, проверяется равенство через оператор
==(протоколEquatable, который является частьюHashable). - Если элемент равен существующему, он не добавляется.
Пример с пользовательским типом:
struct User: Hashable {
let id: UUID
let email: String
// Хэш вычисляется только по полю `id`, так как оно гарантирует уникальность
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
// Равенство также определяется по `id`
static func == (lhs: User, rhs: User) -> Bool {
return lhs.id == rhs.id
}
}
let user1 = User(id: someUUID, email: "alice@example.com")
let user2 = User(id: someUUID, email: "alice.new@example.com") // Тот же ID!
var usersSet: Set<User> = []
usersSet.insert(user1)
usersSet.insert(user2) // Не добавится, так как id совпадает
print(usersSet.count) // 1
Ключевые требования для корректной работы Set:
- Инвариантность хэша: Хэш-значение элемента не должно меняться за время его жизни в коллекции.
- Согласованность с равенством: Если два элемента равны (
a == b), их хэши обязаны быть равными. Обратное не обязательно: разные элементы могут иметь одинаковый хэш (коллизия), но тогдаSetразрешит её через проверку==.