
集合類型的可變性 (Mutability of Collections) #
1. 核心觀念 #
- 概念解說:在 Swift 中,集合(Arrays, Sets, Dictionaries)的可變性完全取決於它們是被宣告為變數(
var)還是常數(let)。這與集合本身的類型定義無關,而是由宣告方式決定。 - 關鍵語法:
var(Mutable),let(Immutable) - Note:
建立不可變的集合(Immutable collections)是一個好的實踐習慣。這不僅讓程式邏輯更清晰,Swift 編譯器也能針對不可變集合進行效能最佳化。
2. 範例 #
// 如果宣告為 var,集合可以在創建後被修改(新增、刪除、變更項目)
var mutableArray = [1, 2, 3]
mutableArray.append(4)
// 如果宣告為 let,集合的大小和內容都不能改變
let immutableArray = [1, 2, 3]
// immutableArray.append(4) // 這行會報錯邏輯解說:這段程式碼展示了 var 賦予了集合修改內容的能力,而 let 則完全鎖定了集合的內容與長度。
3. C# #
概念對應:C# 的 readonly 關鍵字與 Swift 的 let 行為有本質上的不同。
C# 範例:
public class CollectionExample {
// 即使是 readonly,List 的內容依然可以被修改
public readonly List<int> numbers = new List<int> { 1, 2, 3 };
public void Modify() {
numbers.Add(4); // 在 C# 這是合法的!readonly 只保護 reference 不被重新指派
}
}關鍵差異分析:
- 語法面:Swift 的
let宣告集合時,是真正意義上的「完全不可變」(Deep Immutability)。 - 行為面:這是因為 Swift 的集合類型是 Struct (Value Type),而 C# 的集合是 Class (Reference Type)。在 Swift 中,當你把 Array 指派給
let,代表這個 Value Type 的實體完全不能被更動;而在 C# 中,readonly List僅代表你不能將變數指向另一個 List 物件,但原本 List 內部的資料是可以被修改的。若要在 C# 達到 Swiftlet的效果,通常需要使用ImmutableList<T>或ReadOnlyCollection<T>。
陣列 (Arrays) #
1. 核心觀念 #
- 概念解說:Array 是有序的清單,允許儲存重複的值。Swift 的 Array 是一個泛型集合。
- 關鍵語法:
Array<Element>,[Element],[],.count,.isEmpty,.append(),+=,subscript[],[Range],.insert(),.remove(at:) - Note:
Swift 的 Array 類型與 Foundation 的 NSArray 類別有橋接(Bridged)關係。
2. 範例 #
// 1. 初始化與實字 (Literals)
var someInts: [Int] = [] // 空陣列
var threeDoubles = Array(repeating: 0.0, count: 3) // [0.0, 0.0, 0.0]
var shoppingList = ["Eggs", "Milk"] // 型別推斷為 [String]
// 2. 陣列合併與新增
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
var sixDoubles = threeDoubles + anotherThreeDoubles // 使用 + 串接
shoppingList.append("Flour")
shoppingList += ["Baking Powder", "Chocolate Spread", "Cheese", "Butter"]
// 3. 存取與強大的區間修改 (Range Subscript)
var firstItem = shoppingList[0]
shoppingList[0] = "Six eggs"
// 替換 index 4...6 的三個項目為兩個項目 (會改變陣列總長度)
shoppingList[4...6] = ["Bananas", "Apples"]
// 4. 插入與移除
shoppingList.insert("Maple Syrup", at: 0)
let removedItem = shoppingList.remove(at: 0) // 回傳被移除的項目邏輯解說: Swift 透過 [] 讓空陣列與定義變得非常精簡。特別值得注意的是 + 與 += 運算子,它們讓集合的串接像數值運算一樣直觀。此外,區間修改 (Range Subscript),允許開發者用不同數量的元素替換指定區間,系統會自動處理陣列長度的縮放。
3. C# #
概念對應:
- Swift 的 [T] 對應 C# 的 List
。 - Swift 的 Array(repeating:count:) 類似 C# 的 Enumerable.Repeat().ToList()。
- Swift 的陣列 […] 對應 C# 的 Collection Initializer {…}
C# 範例:
// 1. 初始化與重複預設值
var someInts = new List<int>();
var threeDoubles = Enumerable.Repeat(0.0, 3).ToList();
var shoppingList = new List<string> { "Eggs", "Milk" };
// 2. 列表合併與新增
var anotherThreeDoubles = Enumerable.Repeat(2.5, 3).ToList(); // 補上此對照
var sixDoubles = new List<double>(threeDoubles);
sixDoubles.AddRange(anotherThreeDoubles);
shoppingList.Add("Flour");
// 注意:必須補上這三個項目,後續 RemoveRange(4, 3) 才不會報錯
shoppingList.AddRange(new[] { "Baking Powder", "Chocolate Spread", "Cheese", "Butter" });
// 3. 區間修改 (C# 需組合 RemoveRange 與 InsertRange)
shoppingList.RemoveRange(4, 3);
shoppingList.InsertRange(4, new[] { "Bananas", "Apples" });
// 4. 移除
var removedItem = shoppingList[0];
shoppingList.RemoveAt(0); // C# RemoveAt 不會回傳元素,需手動先取值關鍵差異分析:
- 語法面:Swift 的
+=用於陣列串接比 C# 的AddRange更簡潔。Swift 的 Range Subscript ([4...6] = ...) 是 C# 開發者會非常羨慕的功能。 - 行為面:Swift 的 Array 是 Value Type,C# 的
List<T>是 Reference Type。- 在 Swift:
var a = [1]; var b = a; b.append(2);->a還是[1],只有b變了(Copy-on-Write 機制)。 - 在 C#:
var a = new List<int>{1}; var b = a; b.Add(2);->a也會變成[1, 2],因為它們指向同一個物件。
- 在 Swift:
- 設計 : Swift 的 remove(at:) 會回傳被移除的元素,方便直接使用;C# 則必須在 RemoveAt 之前先手動透過索引讀取值。
集合 (Sets) #
1. 核心觀念 #
- 概念解說:
- Set 是無序且元素唯一的集合。存入 Set 的類型必須遵循
Hashable協定,以便計算雜湊值來確認唯一性。透過屬性與方法來操作集合。 - Swift 提供了圖形化的集合運算方法(參考下圖):
intersection(交集):兩者都有的。(左上)symmetricDifference(對稱差集):兩者其中之一有的,但不是兩者都有的 (A ∪ B - A ∩ B)。(右上)union(聯集):兩者所有的。(左下)subtracting(差集):A 有但 B 沒有的。(右下)
- Set 是無序且元素唯一的集合。存入 Set 的類型必須遵循
- 關鍵語法:
Set<Element>,insert(_:),intersection(_:),symmetricDifference(_:),union(_:),subtracting(_:)

2. 範例 #
let oddDigits: Set<Int> = [1, 3, 5, 7, 9]
let evenDigits: Set<Int> = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set<Int> = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9] (奇數中扣掉質數 3, 5, 7,剩 1, 9)
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9] (奇數與質數的聯集,扣掉共同擁有的 3, 5, 7)邏輯解說: 這些方法都會回傳一個新的 Set,而不會修改原始的 Set,而sorted() 會把它轉為 Array[Element]。
3. C# #
概念對應:
C# 的 HashSet 提供了 IntersectWith, UnionWith, ExceptWith, SymmetricExceptWith。
C# 範例:
var oddDigits = new HashSet<int> { 1, 3, 5, 7, 9 };
var evenDigits = new HashSet<int> { 0, 2, 4, 6, 8 };
// C# 的 HashSet 方法通常是 "原地修改 (In-place modification)"
var tempSet = new HashSet<int>(oddDigits); // 先複製一份以免改到原資料
tempSet.UnionWith(evenDigits);
// tempSet 現在變成聯集結果
// 若要像 Swift 一樣回傳新集合,通常需依賴 LINQ
var unionResult = oddDigits.Union(evenDigits).ToHashSet();關鍵差異分析:
- 行為面:Swift 範例中的
union、subtracting等方法是 Functional 的,它們回傳新集合,不改原集合。C# 的UnionWith等方法是 Imperative 的,直接修改呼叫該方法的集合。 - 語法提示:若要在 Swift 中執行類似 C# 的原地修改,需使用
formUnion,formIntersection等帶有form前綴的方法。
集合成員關係與相等性 (Membership and Equality) #
1. 核心觀念 #
- 概念解說:判斷集合之間的關係。
==:內容完全相同。isSubset(of:):是否為子集 (被包含)。isSuperset(of:):是否為超集 (包含對方)。isStrictSubset(of:)/isStrictSuperset(of:):真子集/真超集 (包含但不相等)。isDisjoint(with:):是否完全沒有交集。
- 關鍵語法:
isSubset(of:),isSuperset(of:),isDisjoint(with:)
2. 範例 #
let houseAnimals: Set<String> = ["🐶", "🐱"]
let farmAnimals: Set<String> = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set<String> = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals) // true
farmAnimals.isSuperset(of: houseAnimals) // true
farmAnimals.isDisjoint(with: cityAnimals) // true邏輯解說: 這些方法直觀地對應了數學集合論中的定義。Emoji 在 Swift 中也是合法的 Character/String,可以直接操作。
3. C# #
概念對應:SetEquals, IsSubsetOf, IsSupersetOf, Overlaps。
C# 範例:
var houseAnimals = new HashSet<string> { "🐶", "🐱" };
var farmAnimals = new HashSet<string> { "🐮", "🐔", "🐑", "🐶", "🐱" };
var cityAnimals = new HashSet<string> { "🐦", "🐭" };
houseAnimals.IsSubsetOf(farmAnimals); // true
farmAnimals.IsSupersetOf(houseAnimals); // true
// C# 檢查是否 "有交集" 用 Overlaps,所以 "無交集" (Disjoint) 要取反
!farmAnimals.Overlaps(cityAnimals); // true關鍵差異分析:
- 語法面:C# 沒有直接名為
IsDisjoint的方法,通常使用!Overlaps來判斷是否分離。 - 行為面:兩者的邏輯定義基本一致。值得一提的是 Swift 的
==運算子直接比較內容值,而 C# 的HashSet若使用==預設是比較參考 (Reference Equality),必須使用SetEquals方法來比較內容物是否相同。
字典 (Dictionaries) #
1. 核心觀念 #
- 概念解說:儲存 Key-Value 對應關係的無序集合。Key 必須是
Hashable。 - 關鍵語法:
[Key: Value],updateValue,removeValue, Subscript key access - Note:
使用 Subscript 語法存取字典時,回傳的是一個 Optional 值,因為該 Key 可能不存在。
2. 範例 #
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// 新增或修改
airports["LHR"] = "London"
// 使用 updateValue 可以取得舊值
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("Old value was \(oldValue)")
}
// 存取值(回傳 Optional)
if let airportName = airports["DUB"] {
print("Airport is \(airportName)")
} else {
print("Not found")
}
// 移除
airports["APL"] = nil // 設為 nil 即移除邏輯解說:
[Key: Value]是標準簡寫。updateValue很有用,它在更新同時會回傳「更新前的值」,適合用來做 Log 或邏輯判斷。- 將某個 Key 的值設為
nil,等同於從字典中刪除該 Key。
3. C# #
概念對應:對應 C# 的 Dictionary<TKey, TValue>。
C# 範例:
// C#
var airports = new Dictionary<string, string> {
{ "YYZ", "Toronto Pearson" },
{ "DUB", "Dublin" }
};
// 存取值 - C# 的索引器若 Key 不存在會拋出 Exception
// string name = airports["INVALID"]; // Throws KeyNotFoundException
// 安全存取方式
if (airports.TryGetValue("DUB", out string airportName)) {
Console.WriteLine($"Airport is {airportName}");
}
// 移除
airports.Remove("APL"); // 不能賦值 null 來移除關鍵差異分析:
- 語法面:
- Swift 的索引存取
dict[key]永遠回傳 Optional,這迫使開發者處理 Key 不存在的情況(更安全)。 - C# 的索引存取
dict[key]假設 Key 存在,否則拋出例外。C# 開發者必須習慣使用TryGetValue或是 Swift 的 Optional Binding (if let)。
- Swift 的索引存取
- 行為面:
- 在 Swift 中,
dict["key"] = nil是刪除操作。 - 在 C# 中,如果 Value Type 是 Reference Type,
dict["key"] = null只是把該 Value 設為 null,Key 依然存在於字典中。這是非常容易混淆的點。 - 迭代 (Iteration):Swift 迭代字典時拿到的是 Tuple
(key, value),非常方便解構;C# 拿到的是KeyValuePair<TKey, TValue>物件,需存取.Key和.Value屬性。
- 在 Swift 中,