File: OrderedDictionary.md

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (224 lines) | stat: -rw-r--r-- 8,188 bytes parent folder | download
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.