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:

UseWhen you needLookup cost
ArrayOrdered items, duplicates allowed, access by positionO(1) by index
SetUnique items, fast membership tests, set algebra; order irrelevantO(1) average
DictionaryLookup of a value by an associated key; order irrelevantO(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.