Set
s are designed for performance. The contains(_:)
method on Set
is O(1) complexity meaning it takes a constant amount of time to perform no matter the size of the set. contains(_:)
on an Array
is O(n) meaning the time to determine if the array contains an element increases as the size of the array increases.
How does a Set
do that? It uses a hashValue
for the items of the Set
and contains an internal dictionary like structure that maps the hashValue
to the list of items with that hashValue
. This makes testing equality of complex structures (like String
) very quick because Set
first tests if two values have the same hashValue
. If the hashValue
s are different, it doesn't need to check the contents of the struct
s themselves.
To test if the Set
contains
a value, it is necessary to only look up the hashValue
in the dictionary and then compare the item to the values that match.
So, for items to be contained in a Set
, it is important to provide a hashing function that:
- Is the same if two structs/objects are equal. (absolute requirement)
- Computes a wide range of
hashValues
so that the items are widely distributed and don't require falling back to the slower check for equality. (for good performance)
Here is an example of a Hashable
struct
that is appropriate for storage in a Set
:
struct Option: Hashable, CustomStringConvertible {
var title: String
var key: String
var value: Int
var description: String { return "{title: "(title)", key: "(key)", value: (value)}" }
func hash(into hasher: inout Hasher) {
hasher.combine(title)
hasher.combine(key)
}
static func ==(lhs: Option, rhs: Option) -> Bool {
return lhs.title == rhs.title && lhs.key == rhs.key
}
}
Note: In this example, only the title
and key
properties are considered for equality. Two structs can be equal even if they have different value
properties. The hash(into:)
function likewise only considers the title
and key
properties.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…