Swift Collections — Arrays, Sets, and Dictionaries Explained
In this tutorial, you will learn about Swift Collections. We cover key concepts, practical examples, and best practices to help you master this topic.
Swift collections provide type-safe containers for grouping data, including ordered arrays with random access, unordered sets with unique elements, and key-value dictionaries for fast lookups, all backed by optimized value-type implementations. This tutorial covers creating and modifying arrays, sets, and dictionaries, understanding their performance characteristics, using functional methods like map, filter, and reduce, and choosing the right collection for your use case.
What You'll Learn
- Creating and modifying Arrays with type-safe element constraints
- Using Sets for unique, unordered collections with O(1) lookup
- Working with Dictionaries for key-value storage and fast retrieval
- Iterating over collections with for-in loops and forEach
- Using map, filter, reduce, and compactMap for transformation
- Understanding performance tradeoffs between collection types
- Slicing and splitting collections with subscripts
Why It Matters
Choosing the wrong collection type is one of the most common performance mistakes. Using an array for uniqueness checks (O(n)) when a set (O(1)) suffices can slow an app dramatically. Swift's type-safe collections catch type mismatches at compile time, preventing runtime errors common in languages like Python or JavaScript.
Real-World Use
Doda Browser's tab manager uses a dictionary for tab lookup by URL (O(1)), a set for unique open domains, and an array for ordered tab display. The URL shortener uses a set to check for existing short codes in O(1) time instead of scanning an array.
Learning Path
flowchart LR A[Swift Basics] --> B[Collections\nYou are here] B --> C[Control Flow] style B fill:#f90,color:#fff
Arrays
Arrays are ordered, random-access collections with O(1) element lookup:
import Foundation
// Creating arrays
var numbers = [1, 2, 3, 4, 5]
let fruits: [String] = ["Apple", "Banana", "Cherry"]
var emptyArray: [Int] = []
// Adding and removing
numbers.append(6)
numbers.insert(0, at: 0)
numbers.remove(at: 3)
print("Numbers: \(numbers)")
// Accessing elements
print("First: \(numbers[0])")
print("Last: \(numbers.last ?? -1)")
print("Count: \(numbers.count)")
print("Is empty: \(numbers.isEmpty)")
// Slicing
let slice = numbers[1...3]
print("Slice: \(Array(slice))")
// Iterating
for fruit in fruits {
print("Fruit: \(fruit)")
}
// Iterating with index
for (index, value) in numbers.enumerated() {
print("Index \(index): \(value)")
}
Output:
Numbers: [0, 1, 2, 4, 5, 6]
First: 0
Last: 6
Count: 6
Is empty: false
Slice: [1, 2, 4]
Fruit: Apple
Fruit: Banana
Fruit: Cherry
Index 0: 0
Index 1: 1
...
Sets
Sets store unique values with O(1) membership testing:
import Foundation
// Creating sets
var tags: Set = ["swift", "ios", "programming"]
let moreTags: Set<String> = ["xcode", "swift"]
// Adding and removing
tags.insert("development")
tags.remove("programming")
print("Tags: \(tags.sorted())")
print("Contains 'swift': \(tags.contains("swift"))")
print("Count: \(tags.count)")
// Set operations
let setA: Set = [1, 2, 3, 4, 5]
let setB: Set = [4, 5, 6, 7, 8]
print("Union: \(setA.union(setB).sorted())")
print("Intersection: \(setA.intersection(setB).sorted())")
print("Subtracting: \(setA.subtracting(setB).sorted())")
print("Symmetric diff: \(setA.symmetricDifference(setB).sorted())")
// Membership
let smallSet: Set = [1, 2]
print("Is subset: \(smallSet.isSubset(of: setA))")
print("Is superset: \(setA.isSuperset(of: smallSet))")
print("Is disjoint: \(setA.isDisjoint(with: smallSet))")
Output:
Tags: ["development", "ios", "swift"]
Contains 'swift': true
Count: 3
Union: [1, 2, 3, 4, 5, 6, 7, 8]
Intersection: [4, 5]
Subtracting: [1, 2, 3]
Symmetric diff: [1, 2, 3, 6, 7, 8]
Is subset: true
Is superset: true
Is disjoint: false
Dictionaries
Dictionaries map keys to values with O(1) average lookup:
import Foundation
// Creating dictionaries
var user: [String: Any] = [
"name": "Alice",
"age": 30,
"email": "alice@example.com"
]
var scores: [String: Int] = [:]
scores["Alice"] = 95
scores["Bob"] = 87
scores["Charlie"] = 92
// Accessing values
print("Name: \(user["name"] ?? "")")
print("Score for Alice: \(scores["Alice"] ?? 0)")
// Modifying
scores["Bob"] = 90 // Update existing key
scores["Diana"] = 88 // Add new key-value pair
scores.removeValue(forKey: "Charlie")
print("Scores: \(scores)")
// Iterating
for (name, score) in scores {
print("\(name): \(score)")
}
// Keys and values collections
let names = Array(scores.keys).sorted()
let allScores = Array(scores.values)
print("Names: \(names)")
print("All scores: \(allScores)")
// Default values
let score = scores["Charlie", default: 0]
print("Charlie's score (default): \(score)")
Output:
Name: Alice
Score for Alice: 95
Scores: ["Bob": 90, "Diana": 88, "Alice": 95]
Alice: 95
Bob: 90
Diana: 88
Names: ["Alice", "Bob", "Diana"]
All scores: [95, 90, 88]
Charlie's score (default): 0
Functional Methods
Swift collections provide powerful functional methods:
import Foundation
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// Map: transform each element
let squared = numbers.map { $0 * $0 }
print("Squared: \(squared)")
// Filter: keep elements matching a condition
let evens = numbers.filter { $0 % 2 == 0 }
print("Evens: \(evens)")
// Reduce: combine all elements
let sum = numbers.reduce(0, +)
let product = numbers.reduce(1, *)
print("Sum: \(sum), Product: \(product)")
// CompactMap: transform and remove nil results
let strings = ["1", "2", "three", "4", "five"]
let parsed = strings.compactMap { Int($0) }
print("Parsed ints: \(parsed)")
// FlatMap: flatten nested collections
let nested = [[1, 2], [3, 4], [5, 6]]
let flattened = nested.flatMap { $0 }
print("Flattened: \(flattened)")
// Chaining
let result = numbers
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
.reduce(0, +)
print("Even squares sum: \(result)")
Output:
Squared: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Evens: [2, 4, 6, 8, 10]
Sum: 55, Product: 3628800
Parsed ints: [1, 2, 4]
Flattened: [1, 2, 3, 4, 5, 6]
Even squares sum: 220
Performance Comparison
| Operation | Array | Set | Dictionary |
|---|---|---|---|
| Lookup by index/key | O(1) | O(1) | O(1) |
| Search for value | O(n) | O(1) | O(1)* |
| Insert | O(1) end, O(n) front | O(1) | O(1) |
| Delete | O(n) | O(1) | O(1) |
| Iteration | O(n) | O(n) | O(n) |
Common Mistakes
- Using Array when Set guarantees uniqueness: If you need unique elements, use Set. Array requires manual duplicate checking (O(n) per insert).
- Forcing dictionary keys with String type: Use enums or custom Hashable types as keys for compile-time safety and performance.
- Not preallocating capacity: If you know the approximate size, use
reserveCapacity()to avoid repeated reallocation during growth. - Using [String: Any] dictionaries: Losing type information requires frequent casting. Use custom structs or Codable instead.
- Copy-on-write surprises: Swift collections are value types. Modifying a copied array does not affect the original, but large copies are expensive.
Practice Questions
When would you use a Set instead of an Array?
- When element uniqueness is required and order does not matter. Sets provide O(1) membership testing compared to Array's O(n).
What is the difference between map and compactMap?
- map transforms every element. compactMap transforms and drops nil results, returning only non-optional values.
How do you create an array with a repeated value?
Array(repeating: 0, count: 5)creates [0, 0, 0, 0, 0].
What does reduce do in Swift?
- reduce combines all elements into a single value using a closure.
sum = array.reduce(0, +).
- reduce combines all elements into a single value using a closure.
Challenge: Write a function that takes an array of words, counts the frequency of each word using a dictionary, and returns the top 3 most frequent words.
Mini Project
Build a shopping cart manager:
- Use a dictionary to store products (key: product ID, value: quantity).
- Use a set to track out-of-stock product IDs.
- Use an array to display products in the order they were added.
- Implement functions to add, remove, update quantity, and calculate total.
- Use map and reduce for functional transformations.
FAQ
What's Next
Learn how to control program flow with conditionals and loops in the Control Flow tutorial.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro