1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
|
# `OrderedDictionary`
An ordered collection of key-value pairs.
## Declaration
```swift
import OrderedCollections
@frozen struct OrderedDictionary<Key: Hashable, Value>
```
## Overview
Like the standard `Dictionary`, ordered dictionaries use a hash table to
ensure that no two entries have the same keys, and to efficiently look up
values corresponding to specific keys. However, like an `Array` (and
unlike `Dictionary`), ordered dictionaries maintain their elements in a
particular user-specified order, and they support efficient random-access
traversal of their entries.
`OrderedDictionary` is a useful alternative to `Dictionary` when the order
of elements is important, or when you need to be able to efficiently access
elements at various positions within the collection.
You can create an ordered dictionary with any key type that conforms to the
`Hashable` protocol.
```swift
let responses: OrderedDictionary = [
200: "OK",
403: "Access forbidden",
404: "File not found",
500: "Internal server error",
]
```
### Equality of Ordered Dictionaries
Two ordered dictionaries are considered equal if they contain the same
elements, and *in the same order*. This matches the concept of equality of
an `Array`, and it is different from the unordered `Dictionary`.
```swift
let a: OrderedDictionary = [1: "one", 2: "two"]
let b: OrderedDictionary = [2: "two", 1: "one"]
a == b // false
b.swapAt(0, 1) // `b` now has value [1: "one", 2: "two"]
a == b // true
```
(`OrderedDictionary` only conforms to `Equatable` when its `Value` is
equatable.)
### Dictionary Operations
`OrderedDictionary` provides many of the same operations as `Dictionary`.
For example, you can look up and add/remove values using the familiar
key-based subscript, returning an optional value:
```swift
var dictionary: OrderedDictionary<String, Int> = [:]
dictionary["one"] = 1
dictionary["two"] = 2
dictionary["three"] // nil
// dictionary is now ["one": 1, "two": 2]
```
If a new entry is added using the subscript setter, it gets appended to the
end of the dictionary. (So that by default, the dictionary contains its
elements in the order they were originally inserted.)
`OrderedDictionary` also implements the variant of this subscript that takes
a default value. Like with `Dictionary`, this is useful when you want to
perform in-place mutations on values:
```swift
let text = "short string"
var counts: OrderedDictionary<Character, Int> = [:]
for character in text {
counts[character, default: 0] += 1
}
// counts is ["s": 2, "h": 1, "o": 1,
// "r": 2, "t": 2, " ": 1,
// "i": 1, "n": 1, "g": 1]
```
If the `Value` type implements reference semantics, or when you need to
perform a series of individual mutations on the values, the closure-based
`updateValue(forKey:default:with:)` method provides an easier-to-use
alternative to the defaulted key-based subscript.
```swift
let text = "short string"
var counts: OrderedDictionary<Character, Int> = [:]
for character in text {
counts.updateValue(forKey: character, default: 0) { value in
value += 1
}
}
// Same result as before
```
(This isn't currently available on the regular `Dictionary`.)
The `Dictionary` type's original `updateValue(_:forKey:)` method is also
available, and so is `index(forKey:)`, grouping/uniquing initializers
(`init(uniqueKeysWithValues:)`, `init(_:uniquingKeysWith:)`,
`init(grouping:by:)`), methods for merging one dictionary with another
(`merge`, `merging`), filtering dictionary entries (`filter(_:)`),
transforming values (`mapValues(_:)`), and a combination of these two
(`compactMapValues(_:)`).
### Sequence and Collection Operations
Ordered dictionaries use integer indices representing offsets from the
beginning of the collection. However, to avoid ambiguity between key-based
and indexing subscripts, `OrderedDictionary` doesn't directly conform to
`Collection`. Instead, it only conforms to `Sequence`, and provides a
random-access collection view over its key-value pairs:
```swift
responses[0] // `nil` (key-based subscript)
responses.elements[0] // `(200, "OK")` (index-based subscript)
```
Because ordered dictionaries need to maintain unique keys, neither
`OrderedDictionary` nor its `elements` view can conform to the full
`MutableCollection` or `RangeReplaceableCollection` protocols. However, they
are able to partially implement requirements: they support mutations
that merely change the order of elements, or just remove a subset of
existing members:
```swift
// Permutation operations from MutableCollection:
func swapAt(_ i: Index, _ j: Index)
func partition(by predicate: (Element) throws -> Bool) rethrows -> Index
func sort() where Element: Comparable
func sort(by predicate: (Element, Element) throws -> Bool) rethrows
func shuffle()
func shuffle<T: RandomNumberGenerator>(using generator: inout T)
// Removal operations from RangeReplaceableCollection:
func removeAll(keepingCapacity: Bool = false)
func remove(at index: Index) -> Element
func removeSubrange(_ bounds: Range<Int>)
func removeLast() -> Element
func removeLast(_ n: Int)
func removeFirst() -> Element
func removeFirst(_ n: Int)
func removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrows
```
`OrderedDictionary` also implements `reserveCapacity(_)` from
`RangeReplaceableCollection`, to allow for efficient insertion of a known
number of elements. (However, unlike `Array` and `Dictionary`,
`OrderedDictionary` does not provide a `capacity` property.)
### Keys and Values Views
Like the standard `Dictionary`, `OrderedDictionary` provides `keys` and
`values` properties that provide lightweight views into the corresponding
parts of the dictionary.
The `keys` collection is of type `OrderedSet<Key>`, containing all the keys
in the original dictionary.
```swift
let d: OrderedDictionary = [2: "two", 1: "one", 0: "zero"]
d.keys // [2, 1, 0] as OrderedSet<Int>
```
The `keys` property is read-only, so you cannot mutate the dictionary
through it. However, it returns an ordinary ordered set value, which can be
copied out and then mutated if desired. (Such mutations won't affect the
original dictionary value.)
The `values` collection is a mutable random-access collection of the values
in the dictionary:
```swift
d.values // "two", "one", "zero"
d.values[2] = "nada"
// `d` is now [2: "two", 1: "one", 0: "nada"]
d.values.sort()
// `d` is now [2: "nada", 1: "one", 0: "two"]
```
Both views store their contents in regular `Array` values, accessible
through their `elements` property.
## Performance
Like the standard `Dictionary` type, the performance of hashing operations
in `OrderedDictionary` is highly sensitive to the quality of hashing
implemented by the `Key` type. Failing to correctly implement hashing can
easily lead to unacceptable performance, with the severity of the effect
increasing with the size of the hash table.
In particular, if a certain set of keys all produce the same hash value,
then hash table lookups regress to searching an element in an unsorted
array, i.e., a linear operation. To ensure hashed collection types exhibit
their target performance, it is important to ensure that such collisions
cannot be induced merely by adding a particular list of keys to the
dictionary.
The easiest way to achieve this is to make sure `Key` implements hashing
following `Hashable`'s documented best practices. The conformance must
implement the `hash(into:)` requirement, and every bit of information that
is compared in `==` needs to be combined into the supplied `Hasher` value.
When used correctly, `Hasher` produces high-quality, randomly seeded hash
values that prevent repeatable hash collisions.
When `Key` correctly conforms to `Hashable`, key-based lookups in an ordered
dictionary is expected to take O(1) equality checks on average. Hash
collisions can still occur organically, so the worst-case lookup performance
is technically still O(*n*) (where *n* is the size of the dictionary);
however, long lookup chains are unlikely to occur in practice.
## Implementation Details
An ordered dictionary consists of an ordered set of keys, alongside a
regular `Array` value that contains their associated values.
|