Collections: Arrays, Dictionaries & Sets

SwiftChapter 9 of the Ultimate Swift Series35 min readApril 11, 2026Beginner

In This Article

  1. A Quick Note on Performance (Big-O)
  2. Mutable vs. Immutable Collections
  3. Arrays: Ordered Lists
  4. Accessing Array Elements
  5. Modifying Arrays
  6. Iterating Over Arrays
  7. Array Performance
  8. Dictionaries: Key-Value Pairs
  9. Accessing and Modifying Dictionaries
  10. Iterating Over Dictionaries
  11. Dictionary Performance
  12. Sets: Unique Values
  13. Choosing the Right Collection
  14. Exercises
  15. Key Points

So far, you've stored single values in constants and variables. But real apps deal with collections of data: a list of chat messages, a dictionary of user settings, a set of unique tags. Swift gives you three collection types, each optimized for different use cases.

A Quick Note on Performance (Big-O)

When choosing a collection type, you should consider how fast different operations are. Computer scientists describe this with Big-O notation:

You don't need to memorize formulas. Just know: O(1) is fast, O(n) scales with size. We'll note the performance of key operations for each collection type.

Mutable vs. Immutable Collections

The same let/var rule applies to collections. Use let for collections that won't change, var for ones that will:

let vowels = ["A", "E", "I", "O", "U"] // Immutable — can't add or remove var players = ["Alice", "Bob"] // Mutable — can modify

Arrays: Ordered Lists

An array is an ordered collection of values of the same type. Elements are accessed by their index (position), starting from 0.

// Creating arrays let evenNumbers = [2, 4, 6, 8] // Type: [Int] var names: [String] = [] // Empty array of strings let zeros = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0]

Accessing Array Elements

var players = ["Alice", "Bob", "Cindy", "Dan"] players.isEmpty // false players.count // 4 players.first // Optional("Alice") players.last // Optional("Dan") players.min() // Optional("Alice") — alphabetically first // Subscript access — NOT optional, crashes if out of bounds players[0] // "Alice" players[2] // "Cindy" // players[10] // CRASH: Index out of range // Slice — get a range of elements let middle = Array(players[1...2]) // ["Bob", "Cindy"] // Check if element exists players.contains("Bob") // true
Subscript access can crash

Unlike first and last (which return optionals), subscript access like players[5] crashes if the index is out of bounds. Always check count first or use safe patterns like first/last when the array might be empty.

Modifying Arrays

var players = ["Alice", "Bob", "Cindy", "Dan"] // Append to the end players.append("Eli") players += ["Frank"] // Also appends // Insert at a specific position players.insert("Gina", at: 0) // Gina is now first // Remove elements players.removeLast() // Removes Frank, returns "Frank" players.remove(at: 2) // Removes whoever is at index 2 // Update by subscript players[0] = "Georgia" // Replace first element // Sort players.sort() // Sorts in place (alphabetically) let sorted = players.sorted() // Returns new sorted array, original unchanged

Iterating Over Arrays

let scores = [85, 92, 78, 95, 88] // Simple iteration for score in scores { print(score) } // With index for (index, score) in scores.enumerated() { print("\(index + 1). \(score)") } // Sum all elements var total = 0 for score in scores { total += score } print("Total: \(total)") // 438

Array Performance

OperationDescriptionTime
Access by indexarray[i]O(1)
Append to endarray.append(x)O(1)*
Insert at beginningarray.insert(x, at: 0)O(n)
Remove lastarray.removeLast()O(1)
Remove at indexarray.remove(at: i)O(n)
Searcharray.contains(x)O(n)

*Amortized O(1) — occasionally O(n) when the array needs to resize.

Dictionaries: Key-Value Pairs

A dictionary is an unordered collection of key-value pairs. Keys must be unique and Hashable. Values are accessed by key, not by index.

// Creating dictionaries var scores = ["Alice": 92, "Bob": 85, "Cindy": 78] // [String: Int] var emptyDict: [String: Int] = [:] // Empty dictionary

Accessing and Modifying Dictionaries

// Access — always returns an optional (nil if key not found) scores["Alice"] // Optional(92) scores["Zack"] // nil // Add or update scores["Dan"] = 95 // Adds new pair scores["Alice"] = 98 // Updates Alice's score // Remove scores["Bob"] = nil // Removes Bob entirely // Properties scores.isEmpty // false scores.count // 3
Dictionaries return optionals

Unlike arrays (which crash on bad indices), dictionary subscripts return nil when the key doesn't exist. This is safer but means you need to unwrap the result with if let, guard let, or ??.

Iterating Over Dictionaries

// Iterate over key-value pairs (order is not guaranteed) for (name, score) in scores { print("\(name): \(score)") } // Iterate over just keys or values for name in scores.keys { print(name) }

Dictionary Performance

OperationDescriptionTime
Access by keydict["key"]O(1)
Insert/updatedict["key"] = valueO(1)
Removedict["key"] = nilO(1)
Searchdict["key"] != nilO(1)

Everything is O(1). Dictionaries trade ordering for speed. If you need fast lookups by a key, dictionaries are the right choice.

Sets: Unique Values

A set is an unordered collection of unique values. Duplicates are automatically removed. Elements must be Hashable.

// Creating sets var colors: Set<String> = ["red", "green", "blue", "red"] print(colors) // {"green", "blue", "red"} — duplicate removed, no order print(colors.count) // 3 // Check membership — O(1), this is why sets are useful colors.contains("red") // true colors.contains("purple") // false // Add and remove colors.insert("purple") // Now 4 elements colors.remove("green") // Returns "green", or nil if not found

Sets are ideal when you need to answer "is X in this collection?" as fast as possible, or when you need to guarantee uniqueness.

Choosing the Right Collection

NeedUseWhy
Ordered list, access by positionArrayO(1) index access, maintains order
Look up values by a keyDictionaryO(1) key lookup, no ordering needed
Check membership, guarantee uniquenessSetO(1) contains check, auto-deduplication

Exercises

Try These in Your Playground

  1. Create an array of your 5 favorite foods. Print the first and last items. Append a 6th item. Remove the 3rd item.
  2. Write a function removingOnce(_ item: Int, from array: [Int]) -> [Int] that removes only the first occurrence of an item.
  3. Write a function reversed(_ array: [Int]) -> [Int] that reverses an array without using the built-in reversed() method.
  4. Create a dictionary mapping country codes to country names (e.g., ["US": "United States", "JP": "Japan"]). Add a new country. Look up a code that doesn't exist and handle the nil result.
  5. Write a function occurrencesOfCharacters(in text: String) -> [Character: Int] that counts how often each character appears in a string.
  6. Create a set from the array [1, 2, 3, 2, 1, 4]. Print the count. Check if it contains 5.
  7. Challenge: Write a function isInvertible(_ dict: [String: Int]) -> Bool that returns true if all values in the dictionary are unique. (Hint: use a Set.)

Key Points

What You Learned

This chapter completes Section II: Collection Types of the Swift fundamentals. In the next chapter, we'll begin Section III: Building Your Own Types — starting with closures, then moving into structs, classes, and protocols.

Watch the video lessons

Our Swift Fundamentals course has 27 dedicated video lessons on collections — arrays, dictionaries, and real-world data patterns.

Watch Swift Videos