Skip to main content

[From C# to Swift] 04. Collection Types

Learning Swift from a C# Perspective

Swift : Collection Types

Mutability of Collections
#

1. Core Concepts
#

  • Concept Explanation: In Swift, the mutability of collections (Arrays, Sets, Dictionaries) depends entirely on whether they are declared as a variable (var) or a constant (let). This is independent of the collection’s type definition but determined by the declaration method.
  • Key Syntax: var (Mutable), let (Immutable)
  • Official Tip:

Creating immutable collections is a good practice. It not only makes the program logic clearer but also allows the Swift compiler to optimize performance for immutable collections.

2. Example Analysis
#

Source Code:

// If declared as var, the collection can be modified after creation (add, remove, change items)
var mutableArray = [1, 2, 3]
mutableArray.append(4) 

// If declared as let, the collection's size and content cannot be changed
let immutableArray = [1, 2, 3]
// immutableArray.append(4) // This line will report an error

Logic Explanation: This code demonstrates that var grants the ability to modify the collection’s content, while let completely locks down the content and length.

3. C# Developer Perspective
#

Concept Mapping: The C# readonly keyword is fundamentally different from Swift’s let behavior.

C# Comparison:

// C#
public class CollectionExample {
    // Even if readonly, the content of the List can still be modified
    public readonly List<int> numbers = new List<int> { 1, 2, 3 };

    public void Modify() {
        numbers.Add(4); // In C#, this is legal! readonly only protects the reference from being reassigned
    }
}

Key Differences Analysis:

  • Syntax: Swift’s let declaration for collections represents true “Deep Immutability”.
  • Behavior: This is because Swift collection types are Structs (Value Types), whereas C# collections are Classes (Reference Types). In Swift, assigning an Array to let means this Value Type instance cannot be altered at all; in C#, a readonly List only means you cannot point the variable to another List object, but the internal data of the original List can be modified. To achieve the effect of Swift’s let in C#, you typically need ImmutableList<T> or ReadOnlyCollection<T>.

Arrays
#

1. Core Concepts
#

  • Concept Explanation: An Array is an ordered list that allows storing duplicate values. Swift’s Array is a generic collection.
  • Key Syntax: [Element], Array<Element>, append, +=, Subscript syntax []
  • Official Tip:

Swift’s Array type is bridged to Foundation’s NSArray class.

2. Example Analysis
#

Source Code:

// Shorthand syntax and initialization
var shoppingList: [String] = ["Eggs", "Milk"]

// Use append to add
shoppingList.append("Flour")

// Use += to concatenate arrays
shoppingList += ["Baking Powder", "Chocolate Spread", "Cheese", "Butter"]

// Use Range Subscript to modify values within a range
shoppingList[4...6] = ["Bananas", "Apples"] 
// Replace the three elements from index 4 to 6 with two new elements

Logic Explanation:

  1. Swift prefers the [Type] shorthand syntax.
  2. The += operator can be used to concatenate arrays, which is very intuitive.
  3. The most powerful feature is Range Subscript, which allows directly replacing a range within an array, and the new content length does not have to match the original range length (as seen in the example where 3 elements are replaced by 2).

3. C# Developer Perspective
#

Concept Mapping: Swift’s Array corresponds to C#’s List<T>, not C#’s primitive array T[].

C# Comparison:

// C#
var shoppingList = new List<string> { "Eggs", "Milk" };
shoppingList.Add("Flour");
shoppingList.AddRange(new [] { "Baking Powder", "Chocolate Spread" });

// C# List does not have native Range Assignment syntax; typically requires RemoveRange + InsertRange
shoppingList.RemoveRange(4, 3);
shoppingList.InsertRange(4, new [] { "Bananas", "Apples" });

Key Differences Analysis:

  • Syntax: Swift’s += for array concatenation is cleaner than C#’s AddRange. Swift’s Range Subscript ([4...6] = ...) is a feature C# developers will envy.
  • Behavior: This is the biggest pitfall. Swift Arrays are Value Types; C#’s List<T> are Reference Types.
    • In Swift: var a = [1]; var b = a; b.append(2); -> a remains [1], only b changes (Copy-on-Write mechanism).
    • In C#: var a = new List<int>{1}; var b = a; b.Add(2); -> a also becomes [1, 2] because they point to the same object.

Sets
#

1. Core Concepts
#

  • Concept Explanation: A Set is an unordered collection of unique elements. Types stored in a Set must conform to the Hashable protocol to verify uniqueness via hash values.
  • Key Syntax: Set<Element>, insert, intersection, union, symmetricDifference

2. Example Analysis
#

Source Code:

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]

// Set operations
let oddDigits: Set<Int> = [1, 3, 5, 7, 9]
let evenDigits: Set<Int> = [0, 2, 4, 6, 8]

// Intersection
oddDigits.intersection(evenDigits).sorted() // []
// Union
oddDigits.union(evenDigits).sorted() // [0, 1, ..., 9]

Logic Explanation: Swift provides set operations (intersection, union, difference) directly as core methods of Set, with syntax very close to mathematical definitions.

3. C# Developer Perspective
#

Concept Mapping: Corresponds to C#’s HashSet<T>.

C# Comparison:

// C#
var favoriteGenres = new HashSet<string> { "Rock", "Classical", "Hip hop" };

var oddDigits = new HashSet<int> { 1, 3, 5, 7, 9 };
var evenDigits = new HashSet<int> { 0, 2, 4, 6, 8 };

// C#'s HashSet modifies itself directly (UnionWith, IntersectWith)
// To generate a new set, use LINQ or a copy constructor
var union = new HashSet<int>(oddDigits);
union.UnionWith(evenDigits);

Key Differences Analysis:

  • Syntax: Swift’s Set has no shorthand initialization syntax (like [Type] for Array). You must write Set<Type>, though you can use Array Literals [] for assignment.
  • Behavior: Swift’s set operation methods (like union) usually return a new Set (Functional style), whereas C#’s HashSet methods (like UnionWith) are usually void and modify the current instance (Imperative style). To modify the set itself in Swift, use methods prefixed with form, such as formUnion.

Dictionaries
#

1. Core Concepts
#

  • Concept Explanation: An unordered collection storing Key-Value pairs. Keys must be Hashable.
  • Key Syntax: [Key: Value], updateValue, removeValue, Subscript key access
  • Official Tip:

Accessing a dictionary using Subscript syntax returns an Optional value because the Key might not exist.

2. Example Analysis
#

Source Code:

var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

// Add or modify
airports["LHR"] = "London"

// Use updateValue to get the old value
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("Old value was \(oldValue)")
}

// Access value (returns Optional)
if let airportName = airports["DUB"] {
    print("Airport is \(airportName)")
} else {
    print("Not found")
}

// Remove
airports["APL"] = nil // Setting to nil removes it

Logic Explanation:

  1. [Key: Value] is standard shorthand.
  2. updateValue is useful; it returns “the value before the update,” suitable for logging or logic checks.
  3. Setting a Key’s value to nil is equivalent to deleting that Key from the dictionary.

3. C# Developer Perspective
#

Concept Mapping: Corresponds to C#’s Dictionary<TKey, TValue>.

C# Comparison:

// C#
var airports = new Dictionary<string, string> {
    { "YYZ", "Toronto Pearson" },
    { "DUB", "Dublin" }
};

// Access value - C# indexer throws an Exception if Key does not exist
// string name = airports["INVALID"]; // Throws KeyNotFoundException

// Safe access method
if (airports.TryGetValue("DUB", out string airportName)) {
    Console.WriteLine($"Airport is {airportName}");
}

// Remove
airports.Remove("APL"); // Cannot assign null to remove

Key Differences Analysis:

  • Syntax:
    • Swift’s index access dict[key] always returns Optional, forcing developers to handle cases where the Key doesn’t exist (safer).
    • C#’s index access dict[key] assumes the Key exists, otherwise it throws an exception. C# developers must get used to TryGetValue or Swift’s Optional Binding (if let).
  • Behavior:
    • In Swift, dict["key"] = nil is a delete operation.
    • In C#, if the Value Type is a Reference Type, dict["key"] = null just sets that Value to null; the Key remains in the dictionary. This is a very confusing point.
    • Iteration: Swift iteration yields a Tuple (key, value), which is easy to destructure; C# yields KeyValuePair<TKey, TValue> objects, requiring .Key and .Value access.
// Swift Iteration
for (code, name) in airports {
    print("\(code): \(name)")
}
// C# Iteration
foreach (var kvp in airports) {
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}