Collections
Swift’s collection types—Array
, Dictionary
, Set
, and tuples—provide powerful, type-safe ways to store and manipulate data. They are optimized with copy-on-write and conform to protocols like Collection
and Sequence
.
Arrays
Ordered, index-based collections that allow duplicates.
Example: Basic Array:
var numbers = [1, 2, 3]
numbers.append(4)
numbers[0] = 0
print(numbers) // [0, 2, 3, 4]
Example: Array Operations:
numbers.insert(5, at: 1)
numbers.remove(at: 2)
print(numbers) // [0, 5, 3, 4]
let slice = numbers[1...2] // [5, 3]
let contains = numbers.contains(5) // true
Dictionaries
Unordered key-value pairs with unique keys.
Example: Basic Dictionary:
var scores: [String: Int] = ["Alice": 90, "Bob": 85]
scores["Charlie"] = 95
scores["Bob"] = nil // Remove key
print(scores) // ["Alice": 90, "Charlie": 95]
Example: Dictionary Operations:
let keys = Array(scores.keys) // ["Alice", "Charlie"]
let values = Array(scores.values) // [90, 95]
let merged = scores.merging(["David": 80]) { current, _ in current }
print(merged) // ["Alice": 90, "Charlie": 95, "David": 80]
Sets
Unordered collections of unique elements.
Example: Basic Set:
var colors: Set<String> = ["Red", "Blue"]
colors.insert("Green")
colors.remove("Red")
print(colors) // ["Blue", "Green"]
Example: Set Operations:
let otherColors: Set = ["Blue", "Yellow"]
let union = colors.union(otherColors) // ["Blue", "Green", "Yellow"]
let intersection = colors.intersection(otherColors) // ["Blue"]
let difference = colors.subtracting(otherColors) // ["Green"]
Tuples
Group multiple values, optionally named, immutable by default.
Example: Basic Tuple:
let point = (x: 1.0, y: 2.0)
print(point.x) // 1.0
let status = (200, "OK")
print(status.0) // 200
Example: Tuple Decomposition:
let (code, message) = status
print("Code: \(code), Message: \(message)")
Collection Protocols
Swift’s collections conform to protocols like Sequence
, Collection
, and MutableCollection
.
Example: Custom Collection:
struct CircularBuffer<T>: Collection {
private var elements: [T]
private let capacity: Int
private var startIndex: Int = 0
init(capacity: Int) {
self.capacity = capacity
self.elements = []
}
var startIndex: Int { return 0 }
var endIndex: Int { return elements.count }
func index(after i: Int) -> Int { return i + 1 }
subscript(position: Int) -> T { return elements[position] }
}
var buffer = CircularBuffer<Int>(capacity: 3)
buffer.elements = [1, 2, 3]
for item in buffer {
print(item) // 1, 2, 3
}
Functional Operations
Collections support functional methods like map
, filter
, reduce
.
Example:
let squares = numbers.map { $0 * $0 } // [0, 25, 9, 16]
let evens = numbers.filter { $0 % 2 == 0 } // [0, 4]
let sum = numbers.reduce(0, +) // 12
let sorted = numbers.sorted { $0 < $1 } // [0, 3, 4, 5]
Lazy Collections
Defer operations until results are needed.
Example:
let lazySquares = numbers.lazy.map { $0 * $0 }
print(Array(lazySquares.prefix(2))) // [0, 25]
Copy-on-Write
Collections use copy-on-write to optimize memory.
Example:
var array1 = [1, 2, 3]
var array2 = array1 // Shares storage
array2.append(4) // Copies storage
print(array1) // [1, 2, 3]
print(array2) // [1, 2, 3, 4]
Best Practices
- Choose Correct Type: Use
Set
for uniqueness,Dictionary
for key-value,Array
for order. - Immutable Collections: Prefer
let
for thread safety. - Functional Methods: Use
map
,filter
for declarative code. - Lazy Operations: Optimize for large datasets.
- Type Safety: Specify element types explicitly in APIs.
- Custom Collections: Conform to
Collection
for advanced needs.
Troubleshooting
- Index Out of Range: Validate indices before access.
- Unexpected Copies: Ensure copy-on-write isn’t overused.
- Set Duplicates: Verify
Hashable
conformance for custom types. - Performance: Profile with Instruments for large collections.
- Thread Safety: Use actors or locks for concurrent access.
Example: Comprehensive Collection Usage
struct Inventory {
var items: [String]
var prices: [String: Double]
var categories: Set<String>
func analyze() -> (count: Int, totalValue: Double, uniqueCategories: Int) {
let count = items.count
let totalValue = prices.values.reduce(0, +)
let uniqueCategories = categories.count
return (count, totalValue, uniqueCategories)
}
func filterByCategory(_ category: String) -> [String] {
return items.filter { categories.contains(category) }
}
}
var inventory = Inventory(
items: ["Laptop", "Phone", "Tablet"],
prices: ["Laptop": 999.99, "Phone": 799.99, "Tablet": 499.99],
categories: ["Electronics", "Portable"]
)
let analysis = inventory.analyze()
print("Items: \(analysis.count), Value: \(analysis.totalValue), Categories: \(analysis.uniqueCategories)")
let electronics = inventory.filterByCategory("Electronics")
print(electronics) // ["Laptop", "Phone", "Tablet"]