Ответ
Нет, не всегда. Set гарантирует уникальность элементов, но эта гарантия зависит от корректной реализации протоколов Hashable и Equatable для типа элемента.
Как это работает:
SetиспользуетhashValueдля быстрого поиска элемента в бакете.- Если хэши совпадают, вызывается оператор
==для точной проверки равенства.
Проблема возникает при некорректной реализации: если hash(into:) и == используют не все значимые свойства объекта, разные объекты могут считаться одинаковыми, и Set их отбросит.
Пример некорректной реализации:
struct User: Hashable {
let id: Int
let name: String
// ОШИБКА: Сравнение только по имени, игнорируется `id`
static func == (lhs: User, rhs: User) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name) // Хэшируется только имя
}
}
let userSet: Set<User> = [
User(id: 1, name: "Alex"),
User(id: 2, name: "Alex") // Set воспримет это как дубликат и оставит только одного "Alex"
]
print(userSet.count) // 1, хотя id разные
Best Practice: Всегда включайте в hash(into:) и == все свойства, определяющие уникальность экземпляра.
Ответ 18+ 🔞
А, ну это классика, блядь! Сейчас объясню на пальцах, как эта штука работает, а то многие думают, что Set — это такая волшебная коробочка, куда кидаешь что угодно, а она сама разберётся.
Так вот, нет, сука! Не всегда!
Эта гарантия уникальности — она не с потолка берётся. Она целиком и полностью зависит от тебя, от того, как ты реализуешь для своего типа два протокола: Hashable и Equatable. Если накосячишь там — пиши пропало, Set тебя обманет, как последнего лоха.
Как оно, блядь, под капотом работает:
- Сначала
Setсмотрит наhashValueтвоего объекта. Это как быстрый предфильтр, чтобы не сравнивать всё подряд, а сразу откинуть заведомо разных. - Если хэши совпали (а они могут совпасть у разных объектов, это называется коллизия, ёпта), тогда уже вступает в дело оператор
==. Вот тут уже идёт точная, дотошная проверка: «А ты точно такой же, сука, или только похож?»
И вот тут-то и начинается пиздец, если ты реализовал эти штуки криво. Представь, что hash(into:) и == используют не все свойства, которые делают твой объект уникальным. Тогда два РАЗНЫХ объекта Set посчитает ОДНИМ И ТЕМ ЖЕ и просто выкинет один нахуй!
Смотри, какой прикол может быть:
struct User: Hashable {
let id: Int // Уникальный айдишник, ёбаный
let name: String
// ОШИБКА, БЛЯДЬ! Сравниваем только по имени, про `id` забыли!
static func == (lhs: User, rhs: User) -> Bool {
return lhs.name == rhs.name // "Ну и чё, что id разные? Имена-то одинаковые!"
}
func hash(into hasher: inout Hasher) {
hasher.combine(name) // Хэшируем тоже только имя, айдишник в пизду
}
}
let userSet: Set<User> = [
User(id: 1, name: "Alex"),
User(id: 2, name: "Alex") // Set посмотрит на это и скажет: "О, второй Алекс? Да пошёл ты нахуй, дубликат!"
]
print(userSet.count) // Выведет 1, ёбта! Хотя мы-то знаем, что там два разных чела!
Вот так из-за кривых рук и получается, что уникальность — хуйня. Золотое правило, блядь, запомни раз и навсегда: в hash(into:) и в операторе == всегда включай ВСЕ свойства, которые определяют, чем один экземпляр твоего типа отличается от другого. Не выёбывайся, не пытайся быть умнее компилятора, а то сам себя и обманешь в итоге.