Реализация наборов Swift аналогична реализации словарей, которая хорошо описана в Изучение реализации Swift Dictionary .В частности, хранилище элементов представляет собой список «блоков», каждое из которых может быть занято или нет.Когда новый элемент вставляется в набор, его хэш-значение используется для определения начального сегмента.Если этот сегмент занят, выполняется линейный поиск следующего свободного сегмента.Аналогично, при поиске элемента в наборе значение хеш-функции используется для определения начального сегмента, а затем выполняется линейный поиск до тех пор, пока не будет найден элемент (или незанятый сегмент).
(подробности могутв реализации с открытым исходным кодом наиболее подходящими исходными файлами являются Set.swift , NativeSet.swift , SetStorage.swift и HashTable.swift.)
Отключение значения хеш-функции вставленного элемента нарушает инварианты реализации хранения набора: определение местоположения элемента через его начальное ведение больше не работает.А изменение других свойств, влияющих на равенство, может привести к появлению нескольких «равных» элементов в одном и том же списке.
Поэтому я думаю, что можно с уверенностью сказать, что
После вставки экземплярассылочный тип в набор, свойства этого экземпляра не должны изменяться таким образом, чтобы это влияло на его хэш-значение или проверку на равенство.
Примеры
Во-первых, это проблема только для наборов ссылочного типа. Набор типа значения содержит независимые копии значения, и изменение свойства этого значения после вставки не влияет на набор:
struct Foo: Hashable {
var x: Int
}
var set = Set<Foo>()
var foo = Foo(x: 1)
set.insert(foo)
print(set.map { $0.x }) // [1]
foo.x = 2
print(set.map { $0.x }) // [1]
set.insert(foo)
print(set.map { $0.x }) // [1, 2]
Экземпляры ссылочного типа являются «указателями» на фактическое хранилище объектов, и изменение свойства этого экземпляра не изменяет ссылку.Поэтому возможно изменить свойство экземпляра после его вставки в набор:
class Bar: Hashable {
var x : Int
init(x: Int) { self.x = x }
static func == (lhs: Bar, rhs: Bar) -> Bool { return lhs.x == rhs.x }
func hash(into hasher: inout Hasher) { hasher.combine(x) }
}
var set = Set<Bar>()
let bar = Bar(x: 1)
set.insert(bar)
print(set.map { $0.x }) // [1]
bar.x = 2
print(set.map { $0.x }) // [2]
Однако это легко приводит к сбоям, например, если мы снова вставим ту же ссылку:
set.insert(bar)
Fatal error: Duplicate elements of type 'Bar' were found in a Set.
This usually means either that the type violates Hashable's requirements, or
that members of such a set were mutated after insertion.
Вот еще один пример, где значение хеша одинаково для всех экземпляров, но изменение свойства, которое используется для проверки на равенство, приводит к набору двух «равных» экземпляров:
class Baz: Hashable {
var x : Int
init(x: Int) { self.x = x }
static func == (lhs: Baz, rhs: Baz) -> Bool { return lhs.x == rhs.x }
func hash(into hasher: inout Hasher) { }
}
var set = Set<Baz>()
let baz1 = Baz(x: 1)
set.insert(baz1)
let baz2 = Baz(x: 2)
set.insert(baz2)
baz1.x = 2
print(set.map { $0.x }) // [2, 2]
print(set.count) // 2
print(Set(Array(set)).count) // 1 ?