Skip to content

Swift Collections — Arrays, Sets, and Dictionaries Explained

DodaTech Updated 2026-06-28 6 min read

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

  1. Using Array when Set guarantees uniqueness: If you need unique elements, use Set. Array requires manual duplicate checking (O(n) per insert).
  2. Forcing dictionary keys with String type: Use enums or custom Hashable types as keys for compile-time safety and performance.
  3. Not preallocating capacity: If you know the approximate size, use reserveCapacity() to avoid repeated reallocation during growth.
  4. Using [String: Any] dictionaries: Losing type information requires frequent casting. Use custom structs or Codable instead.
  5. 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

  1. 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).
  2. What is the difference between map and compactMap?

    • map transforms every element. compactMap transforms and drops nil results, returning only non-optional values.
  3. How do you create an array with a repeated value?

    • Array(repeating: 0, count: 5) creates [0, 0, 0, 0, 0].
  4. What does reduce do in Swift?

    • reduce combines all elements into a single value using a closure. sum = array.reduce(0, +).
  5. 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

Are Swift collections thread-safe?

No. Swift's collection types are not thread-safe. Use locks, serial queues, or actors to coordinate access from multiple threads.

What is the difference between ArraySlice and Array?

ArraySlice is a view into an existing array. It shares storage with the original and is more memory-efficient for subrange operations.

How do I sort a dictionary by keys or values?

Dictionaries are unordered. Convert to an array of key-value pairs and sort: dict.sorted { $0.key < $1.key }.

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