Collection Types
Swift ships three primary collections: arrays for ordered lists, sets for unordered collections of unique values, and dictionaries for key-value lookups. All three are value types with strong typing, so a collection only ever holds the element type you declare. This chapter shows how to use each and when to pick which.
Mutability of collections
Whether a collection can change after creation depends only on how you bind it. Assign a collection to a var and it's mutable — you can add, remove, and change elements. Assign it to a let and it's immutable, with a fixed size and contents.
var mutableScores = [10, 20]
mutableScores.append(30) // fine
let fixedScores = [10, 20]
// fixedScores.append(30) // error: can't mutate a let collection
Tip: Prefer let for collections that won't change. It communicates intent and lets the compiler optimize.
Arrays
An array stores values of the same type in an ordered list, allowing duplicates. The full type is Array<Element>, but the shorthand [Element] is preferred.
Creating arrays
var shoppingList: [String] = ["Eggs", "Milk"]
var emptyInts: [Int] = []
var alsoEmpty = [Int]()
// Repeated default value
var threeDoubles = Array(repeating: 0.0, count: 3) // → [0.0, 0.0, 0.0]
Accessing and modifying arrays
Read the element count with count, check emptiness with isEmpty, and access elements by zero-based subscript. You can append, insert, remove, and replace ranges.
var shoppingList = ["Eggs", "Milk"]
print(shoppingList.count) // → 2
shoppingList.append("Flour")
shoppingList += ["Cheese", "Butter"]
print(shoppingList[0]) // → Eggs
shoppingList[0] = "Six eggs" // replace one element
shoppingList[1...2] = ["Bananas"] // replace a range (count can change)
shoppingList.insert("Syrup", at: 0)
let removed = shoppingList.remove(at: 0) // returns the removed item
let last = shoppingList.removeLast()
Watch out: Subscripting with an out-of-range index crashes. Use array.indices.contains(i) or check against count first if the index might be invalid.
Iterating over an array
Loop directly over the values, or use enumerated() when you also need each item's index.
let list = ["Eggs", "Milk", "Flour"]
for item in list {
print(item)
}
for (index, value) in list.enumerated() {
print("Item \(index + 1): \(value)")
}
// → Item 1: Eggs ... Item 3: Flour
Sets
A set stores distinct values of the same type with no defined ordering. Use a set when order doesn't matter, or when you need to guarantee each element appears only once. The element type must be Hashable — true for all of Swift's basic types.
Creating a set
A set's type can't be inferred from a literal alone (since array literals look the same), so annotate it explicitly as Set<Element>, or write Set<Element>() for an empty one.
var letters = Set<Character>()
var genres: Set<String> = ["Rock", "Classical", "Hip hop"]
print(genres.count) // → 3
Modifying and testing membership
var genres: Set<String> = ["Rock", "Classical"]
genres.insert("Jazz")
genres.insert("Jazz") // no effect: already present
print(genres.contains("Rock")) // → true
if let removed = genres.remove("Rock") {
print("Removed \(removed)") // → Removed Rock
}
// Sets are unordered; sort to iterate predictably
for genre in genres.sorted() {
print(genre)
}
Set algebra
Sets shine at mathematical operations. Combine and compare them with the standard set algebra methods.
let odd: Set = [1, 3, 5, 7, 9]
let even: Set = [0, 2, 4, 6, 8]
let primes: Set = [2, 3, 5, 7]
print(odd.union(even).sorted()) // → [0,1,2,3,4,5,6,7,8,9]
print(odd.intersection(primes).sorted()) // → [3, 5, 7]
print(odd.subtracting(primes).sorted()) // → [1, 9]
print(odd.symmetricDifference(primes).sorted()) // → [1, 2, 9]
Membership and equality use the relationship methods: isSubset(of:), isSuperset(of:), isStrictSubset(of:), isStrictSuperset(of:), and isDisjoint(with:).
let house: Set = ["🐶", "🐱"]
let farm: Set = ["🐶", "🐱", "🐄", "🐔"]
print(house.isSubset(of: farm)) // → true
print(farm.isSuperset(of: house)) // → true
print(house.isDisjoint(with: ["🐟"])) // → true
Dictionaries
A dictionary stores associations between keys and values, with no defined ordering. Each key is unique and must be Hashable. The full type is Dictionary<Key, Value>; the shorthand [Key: Value] is preferred.
Creating a dictionary
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var emptyMap: [Int: String] = [:]
var alsoEmpty = [String: Int]()
Accessing and modifying a dictionary
Subscripting by key returns an optional value, because the key might not exist. Assigning nil via subscript removes the key. updateValue(_:forKey:) returns the old value if there was one.
var airports = ["YYZ": "Toronto Pearson"]
airports["LHR"] = "London" // add a new pair
airports["LHR"] = "London Heathrow" // update an existing pair
if let name = airports["YYZ"] {
print("YYZ is \(name)") // → YYZ is Toronto Pearson
}
// Default value when the key is missing
print(airports["XXX", default: "Unknown"]) // → Unknown
let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB")
airports["LHR"] = nil // remove the LHR pair
Iterating over a dictionary
Iterate to get each key-value pair as a tuple, or walk just the keys or values collection.
let airports = ["YYZ": "Toronto", "DUB": "Dublin"]
for (code, name) in airports {
print("\(code): \(name)")
}
for code in airports.keys.sorted() {
print(code) // → DUB, YYZ
}
let allNames = Array(airports.values)
Choosing the right collection
Each collection answers a different question. Pick by what your data needs:
| Use | When you need | Lookup cost |
|---|---|---|
| Array | Ordered items, duplicates allowed, access by position | O(1) by index |
| Set | Unique items, fast membership tests, set algebra; order irrelevant | O(1) average |
| Dictionary | Lookup of a value by an associated key; order irrelevant | O(1) average by key |
Performance notes
Arrays give constant-time access by index and amortized constant-time appends, but inserting or removing near the front is O(n) because later elements shift. Sets and dictionaries trade ordering for average O(1) membership and lookup, thanks to hashing — that's why their elements (and dictionary keys) must be Hashable. All three are value types using copy-on-write, so passing a collection around is cheap until something mutates it.
Note: If you find yourself scanning an array repeatedly with contains(_:), that's O(n) each time — a Set or a Dictionary keyed lookup is usually the better structure.
Wrapping up
You can now create, read, modify, and iterate Swift's three core collections, reach for set algebra when it fits, and choose between them based on ordering, uniqueness, and lookup needs. With values, operators, strings, and collections covered, the next chapter ties them together with control flow.