集合类型
Swift 提供三种主要的集合类型:数组、集合和字典,用于存储值的集合。数组是值的有序集合。集合是唯一值的无序集合。字典是键值对的无序集合。

Swift 中的数组、集合和字典对于可以存储的值和键的类型始终是明确的。这意味着你不会错误地将类型不匹配的值插入集合中。这也意味着你可以确信从集合中获取的值的类型。
注意 Swift 的数组、集合和字典类型是作为泛型集合实现的。有关泛型类型和集合的更多信息,请参阅泛型。
集合的可变性
如果创建数组、集合或字典并将其赋值给变量,则创建的集合将是可变的。这意味着你可以在创建集合后通过添加、删除或更改集合中的项来更改(或变更)集合。如果将数组、集合或字典赋值给常量,则该集合是不可变的,其大小和内容都不能更改。
注意 在所有不需要更改集合的情况下,最好创建不可变集合。这样做可以让你更容易理解代码,并使 Swift 编译器能够优化你创建的集合的性能。
数组
数组以有序列表的形式存储相同类型的值。相同的值可以在数组中的不同位置多次出现。
注意 Swift 的
Array类型与 Foundation 的NSArray类桥接。有关将
Array与 Foundation 和 Cocoa 一起使用的更多信息,请参阅 Bridging Between Array and NSArray。
数组类型简写语法
Swift 数组的完整类型写作 Array<Element>,其中 Element 是数组允许存储的值的类型。你也可以将数组类型简写为 [Element]。虽然两种形式在功能上是相同的,但简写形式是首选的,本指南在提及数组类型时始终使用简写形式。
创建空数组
你可以使用两种方法在 Swift 中创建空数组。如果上下文已经提供了类型信息,例如函数参数或已经指定类型的变量或常量,你可以使用空数组字面量,写作 [](一对空的方括号):
var someInts: [Int] = []
print("someInts is of type [Int] with \(someInts.count) items.")
// Prints "someInts is of type [Int] with 0 items."
或者,你可以使用显式初始化器语法创建特定类型的空数组,方法是在方括号内写入元素类型,后跟括号——例如,下面的 [Int]():
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// Prints "someInts is of type [Int] with 0 items."
两种方法产生相同的结果。但是,空数组字面量更短,通常更容易阅读。
在这两种情况下,你都可以使用空数组字面量([])将空数组重新赋值给现有变量:
someInts.append(3)
// someInts now contains 1 value of type Int
someInts = []
// someInts is now an empty array, but is still of type [Int]
使用默认值创建数组
Swift 的 Array 类型还提供了一个初始化器,用于创建特定大小的数组,其中所有值都设置为相同的默认值。你向这个初始化器传递适当类型的默认值(称为 repeating)以及该值在新数组中重复的次数(称为 count):
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
通过两个数组相加创建数组
你可以使用加法运算符(+)将两个兼容类型的现有数组相加来创建新数组。新数组的类型从你相加的两个数组的类型推断出来:
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
使用数组字面量创建数组
你也可以使用数组字面量来初始化数组,这是一种将一个或多个值写成数组集合的简便方法。数组字面量写成一个值列表,用逗号分隔,由一对方括号包围:
[<#value 1#>, <#value 2#>, <#value 3#>]
下面的示例创建一个名为 shoppingList 的数组来存储 String 值:
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList has been initialized with two initial items
shoppingList 变量被声明为”字符串值数组”,写作 [String]。因为这个特定数组指定了值类型为 String,所以它只允许存储 String 值。这里,shoppingList 数组用两个 String 值("Eggs" 和 "Milk")初始化,写在数组字面量中。
注意
shoppingList数组被声明为变量(使用var引入符),而不是常量(使用let引入符),因为在下面的示例中会向购物清单添加更多项。
在这种情况下,数组字面量包含两个 String 值,没有其他内容。这与 shoppingList 变量声明的类型(只能包含 String 值的数组)匹配,因此允许将数组字面量赋值作为用两个初始项初始化 shoppingList 的方式。
由于 Swift 的类型推断,如果你使用包含相同类型值的数组字面量初始化数组,则不必编写数组的类型。shoppingList 的初始化可以用更短的形式编写:
var shoppingList = ["Eggs", "Milk"]
因为数组字面量中的所有值都是相同类型,Swift 可以推断 [String] 是用于 shoppingList 变量的正确类型。
访问和修改数组
你可以通过数组的方法和属性,或使用下标语法来访问和修改数组。
要找出数组中的项数,检查其只读 count 属性:
print("The shopping list contains \(shoppingList.count) items.")
// Prints "The shopping list contains 2 items."
使用布尔 isEmpty 属性作为检查 count 属性是否等于 0 的快捷方式:
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list isn't empty.")
}
// Prints "The shopping list isn't empty."
你可以通过调用数组的 append(_:) 方法将新项添加到数组末尾:
shoppingList.append("Flour")
// shoppingList now contains 3 items, and someone is making pancakes
或者,使用加法赋值运算符(+=)追加一个或多个兼容项的数组:
shoppingList += ["Baking Powder"]
// shoppingList now contains 4 items
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList now contains 7 items
使用下标语法从数组中检索值,在数组名称后面的方括号内传递要检索的值的索引:
var firstItem = shoppingList[0]
// firstItem is equal to "Eggs"
注意 数组中的第一项索引为
0,而不是1。Swift 中的数组始终从零开始索引。
你可以使用下标语法更改给定索引处的现有值:
shoppingList[0] = "Six eggs"
// the first item in the list is now equal to "Six eggs" rather than "Eggs"
使用下标语法时,指定的索引需要是有效的。例如,写 shoppingList[shoppingList.count] = "Salt" 来尝试将项追加到数组末尾会导致运行时错误。
你还可以使用下标语法一次更改一个范围的值,即使替换值集的长度与要替换的范围不同。以下示例将 "Chocolate Spread"、"Cheese" 和 "Butter" 替换为 "Bananas" 和 "Apples":
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList now contains 6 items
要在指定索引处将项插入数组,调用数组的 insert(_:at:) 方法:
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList now contains 7 items
// "Maple Syrup" is now the first item in the list
这次对 insert(_:at:) 方法的调用在购物清单的最开头插入一个值为 "Maple Syrup" 的新项,由索引 0 指示。
类似地,使用 remove(at:) 方法从数组中删除项。此方法删除指定索引处的项并返回删除的项(尽管如果不需要,可以忽略返回值):
let mapleSyrup = shoppingList.remove(at: 0)
// the item that was at index 0 has just been removed
// shoppingList now contains 6 items, and no Maple Syrup
// the mapleSyrup constant is now equal to the removed "Maple Syrup" string
注意 如果尝试访问或修改超出数组现有边界的索引的值,将触发运行时错误。你可以在使用索引之前通过将其与数组的
count属性进行比较来检查索引是否有效。数组中最大的有效索引是count - 1,因为数组从零开始索引——但是,当count为0(意味着数组为空)时,没有有效的索引。
当删除项时,数组中的任何间隙都会被关闭,因此索引 0 处的值再次等于 "Six eggs":
firstItem = shoppingList[0]
// firstItem is now equal to "Six eggs"
如果要从数组中删除最后一项,使用 removeLast() 方法而不是 remove(at:) 方法,以避免需要查询数组的 count 属性。与 remove(at:) 方法一样,removeLast() 返回删除的项:
let apples = shoppingList.removeLast()
// the last item in the array has just been removed
// shoppingList now contains 5 items, and no apples
// the apples constant is now equal to the removed "Apples" string
遍历数组
你可以使用 for-in 循环遍历数组中的整个值集:
for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
如果你需要每个项的整数索引及其值,使用 enumerated() 方法来遍历数组。对于数组中的每个项,enumerated() 方法返回一个由整数和项组成的元组。整数从零开始,每个项加一;如果枚举整个数组,这些整数与项的索引匹配。你可以在迭代过程中将元组分解为临时常量或变量:
for (index, value) in shoppingList.enumerated() {
print("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
有关 for-in 循环的更多信息,请参阅 For-In 循环。
集合
集合在没有定义顺序的集合中存储相同类型的不同值。当项的顺序不重要时,或者当你需要确保项只出现一次时,可以使用集合代替数组。
注意 Swift 的
Set类型与 Foundation 的NSSet类桥接。有关将
Set与 Foundation 和 Cocoa 一起使用的更多信息,请参阅 Bridging Between Set and NSSet。
集合类型的哈希值
类型必须是可哈希的才能存储在集合中——也就是说,该类型必须提供一种为自身计算哈希值的方法。哈希值是一个 Int 值,对于所有比较相等的对象都相同,因此如果 a == b,则 a 的哈希值等于 b 的哈希值。
Swift 的所有基本类型(如 String、Int、Double 和 Bool)默认都是可哈希的,可以用作集合值类型或字典键类型。没有关联值的枚举 case 值(如枚举中所述)默认也是可哈希的。
注意 你可以通过使自定义类型遵循 Swift 标准库中的
Hashable协议,将其用作集合值类型或字典键类型。有关实现所需的hash(into:)方法的信息,请参阅 Hashable。有关遵循协议的信息,请参阅协议。
集合类型语法
Swift 集合的类型写作 Set<Element>,其中 Element 是集合允许存储的类型。与数组不同,集合没有等效的简写形式。
创建和初始化空集合
你可以使用初始化器语法创建特定类型的空集合:
var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// Prints "letters is of type Set<Character> with 0 items."
注意
letters变量的类型从初始化器的类型推断为Set<Character>。
或者,如果上下文已经提供了类型信息,例如函数参数或已经指定类型的变量或常量,你可以使用空数组字面量创建空集合:
letters.insert("a")
// letters now contains 1 value of type Character
letters = []
// letters is now an empty set, but is still of type Set<Character>
使用数组字面量创建集合
你也可以使用数组字面量初始化集合,作为将一个或多个值写成集合的简便方法。
下面的示例创建一个名为 favoriteGenres 的集合来存储 String 值:
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres has been initialized with three initial items
favoriteGenres 变量被声明为”String 值的集合”,写作 Set<String>。因为这个特定集合指定了值类型为 String,所以它只允许存储 String 值。这里,favoriteGenres 集合用三个 String 值("Rock"、"Classical" 和 "Hip hop")初始化,写在数组字面量中。
注意
favoriteGenres集合被声明为变量(使用var引入符),而不是常量(使用let引入符),因为在下面的示例中会添加和删除项。
仅从数组字面量无法推断集合类型,因此必须显式声明 Set 类型。但是,由于 Swift 的类型推断,如果你使用仅包含一种类型值的数组字面量初始化集合,则不必编写集合元素的类型。favoriteGenres 的初始化可以用更短的形式编写:
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
因为数组字面量中的所有值都是相同类型,Swift 可以推断 Set<String> 是用于 favoriteGenres 变量的正确类型。
访问和修改集合
你可以通过集合的方法和属性来访问和修改集合。
要找出集合中的项数,检查其只读 count 属性:
print("I have \(favoriteGenres.count) favorite music genres.")
// Prints "I have 3 favorite music genres."
使用布尔 isEmpty 属性作为检查 count 属性是否等于 0 的快捷方式:
if favoriteGenres.isEmpty {
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
// Prints "I have particular music preferences."
你可以通过调用集合的 insert(_:) 方法将新项添加到集合中:
favoriteGenres.insert("Jazz")
// favoriteGenres now contains 4 items
你可以通过调用集合的 remove(_:) 方法从集合中删除项,如果该项是集合的成员,则删除该项并返回删除的值,如果集合不包含该项,则返回 nil。或者,可以使用 removeAll() 方法删除集合中的所有项。
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// Prints "Rock? I'm over it."
要检查集合是否包含特定项,使用 contains(_:) 方法。
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// Prints "It's too funky in here."
遍历集合
你可以使用 for-in 循环遍历集合中的值。
for genre in favoriteGenres {
print("\(genre)")
}
// Classical
// Jazz
// Hip hop
有关 for-in 循环的更多信息,请参阅 For-In 循环。
Swift 的 Set 类型没有定义的顺序。要按特定顺序遍历集合的值,使用 sorted() 方法,它将集合的元素作为使用 < 运算符排序的数组返回。
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical
// Hip hop
// Jazz
执行集合操作
你可以高效地执行基本的集合操作,例如将两个集合组合在一起、确定两个集合有哪些共同值,或确定两个集合是否包含全部、部分或没有相同的值。
基本集合操作
下图描绘了两个集合——a 和 b——各种集合操作的结果由阴影区域表示。

- 使用
intersection(_:)方法创建一个仅包含两个集合共有值的新集合。 - 使用
symmetricDifference(_:)方法创建一个包含任一集合中的值但不包含两者共有值的新集合。 - 使用
union(_:)方法创建一个包含两个集合中所有值的新集合。 - 使用
subtracting(_:)方法创建一个不包含指定集合中的值的新集合。
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [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]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
集合成员关系和相等性
下图描绘了三个集合——a、b 和 c——重叠区域表示集合之间共享的元素。集合 a 是集合 b 的超集,因为 a 包含 b 中的所有元素。相反,集合 b 是集合 a 的子集,因为 b 中的所有元素也包含在 a 中。集合 b 和集合 c 彼此不相交,因为它们没有共同的元素。

- 使用”相等”运算符(
==)确定两个集合是否包含所有相同的值。 - 使用
isSubset(of:)方法确定集合的所有值是否都包含在指定集合中。 - 使用
isSuperset(of:)方法确定集合是否包含指定集合中的所有值。 - 使用
isStrictSubset(of:)或isStrictSuperset(of:)方法确定集合是否是指定集合的子集或超集,但不等于指定集合。 - 使用
isDisjoint(with:)方法确定两个集合是否没有共同值。
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
字典
字典在没有定义顺序的集合中存储相同类型的键和相同类型的值之间的关联。每个值都与唯一的键相关联,键充当该值在字典中的标识符。与数组中的项不同,字典中的项没有指定的顺序。当你需要根据标识符查找值时,使用字典,就像现实世界中的字典用于查找特定单词的定义一样。
注意 Swift 的
Dictionary类型与 Foundation 的NSDictionary类桥接。有关将
Dictionary与 Foundation 和 Cocoa 一起使用的更多信息,请参阅 Bridging Between Dictionary and NSDictionary。
字典类型简写语法
Swift 字典的完整类型写作 Dictionary<Key, Value>,其中 Key 是可用作字典键的值的类型,Value 是字典为这些键存储的值的类型。
注意 字典的
Key类型必须遵循Hashable协议,就像集合的值类型一样。
你也可以将字典类型简写为 [Key: Value]。虽然两种形式在功能上是相同的,但简写形式是首选的,本指南在提及字典类型时始终使用简写形式。
创建空字典
与数组一样,你可以使用初始化器语法创建特定类型的空 Dictionary:
var namesOfIntegers: [Int: String] = [:]
// namesOfIntegers is an empty [Int: String] dictionary
此示例创建一个类型为 [Int: String] 的空字典来存储整数值的人类可读名称。它的键是 Int 类型,它的值是 String 类型。
如果上下文已经提供了类型信息,你可以使用空字典字面量创建空字典,写作 [:](方括号内的冒号):
namesOfIntegers[16] = "sixteen"
// namesOfIntegers now contains 1 key-value pair
namesOfIntegers = [:]
// namesOfIntegers is once again an empty dictionary of type [Int: String]
使用字典字面量创建字典
你也可以使用字典字面量初始化字典,其语法与前面看到的数组字面量类似。字典字面量是将一个或多个键值对写成 Dictionary 集合的简便方法。
键值对是键和值的组合。在字典字面量中,每个键值对中的键和值用冒号分隔。键值对写成一个列表,用逗号分隔,由一对方括号包围:
[<#key 1#>: <#value 1#>, <#key 2#>: <#value 2#>, <#key 3#>: <#value 3#>]
下面的示例创建一个字典来存储国际机场的名称。在这个字典中,键是三个字母的国际航空运输协会代码,值是机场名称:
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
airports 字典被声明为类型 [String: String],意思是”一个键为 String 类型、值也为 String 类型的 Dictionary”。
注意
airports字典被声明为变量(使用var引入符),而不是常量(使用let引入符),因为在下面的示例中会向字典添加更多机场。
airports 字典使用包含两个键值对的字典字面量初始化。第一对的键是 "YYZ",值是 "Toronto Pearson"。第二对的键是 "DUB",值是 "Dublin"。
这个字典字面量包含两个 String: String 对。这种键值类型与 airports 变量声明的类型(只有 String 键和只有 String 值的字典)匹配,因此允许将字典字面量赋值作为用两个初始项初始化 airports 字典的方式。
与数组一样,如果你使用键和值具有一致类型的字典字面量初始化字典,则不必编写字典的类型。airports 的初始化可以用更短的形式编写:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
因为字面量中的所有键都是相同类型,同样所有值也是相同类型,Swift 可以推断 [String: String] 是用于 airports 字典的正确类型。
访问和修改字典
你可以通过字典的方法和属性,或使用下标语法来访问和修改字典。
与数组一样,你可以通过检查只读 count 属性来查找 Dictionary 中的项数:
print("The airports dictionary contains \(airports.count) items.")
// Prints "The airports dictionary contains 2 items."
使用布尔 isEmpty 属性作为检查 count 属性是否等于 0 的快捷方式:
if airports.isEmpty {
print("The airports dictionary is empty.")
} else {
print("The airports dictionary isn't empty.")
}
// Prints "The airports dictionary isn't empty."
你可以使用下标语法向字典添加新项。使用适当类型的新键作为下标索引,并分配适当类型的新值:
airports["LHR"] = "London"
// the airports dictionary now contains 3 items
你也可以使用下标语法更改与特定键关联的值:
airports["LHR"] = "London Heathrow"
// the value for "LHR" has been changed to "London Heathrow"
作为下标的替代方案,使用字典的 updateValue(_:forKey:) 方法设置或更新特定键的值。与上面的下标示例一样,如果键不存在,updateValue(_:forKey:) 方法会设置一个值,如果键已经存在,则更新该值。但是,与下标不同,updateValue(_:forKey:) 方法在执行更新后返回旧值。这使你能够检查是否进行了更新。
updateValue(_:forKey:) 方法返回字典值类型的可选值。例如,对于存储 String 值的字典,该方法返回类型为 String? 或”可选 String”的值。如果更新前该键存在旧值,则此可选值包含该键的旧值,如果不存在值,则为 nil:
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// Prints "The old value for DUB was Dublin."
你也可以使用下标语法从字典中检索特定键的值。因为可能请求不存在值的键,字典的下标返回字典值类型的可选值。如果字典包含请求键的值,下标返回包含该键现有值的可选值。否则,下标返回 nil:
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport isn't in the airports dictionary.")
}
// Prints "The name of the airport is Dublin Airport."
你可以使用下标语法通过为该键分配 nil 值来从字典中删除键值对:
airports["APL"] = "Apple International"
// "Apple International" isn't the real airport for APL, so delete it
airports["APL"] = nil
// APL has now been removed from the dictionary
或者,使用 removeValue(forKey:) 方法从字典中删除键值对。此方法在键值对存在时删除它并返回删除的值,如果不存在值则返回 nil:
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary doesn't contain a value for DUB.")
}
// Prints "The removed airport's name is Dublin Airport."
遍历字典
你可以使用 for-in 循环遍历字典中的键值对。字典中的每个项都作为 (key, value) 元组返回,你可以在迭代过程中将元组的成员分解为临时常量或变量:
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// LHR: London Heathrow
// YYZ: Toronto Pearson
有关 for-in 循环的更多信息,请参阅 For-In 循环。
你还可以通过访问字典的 keys 和 values 属性来检索字典键或值的可迭代集合:
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: LHR
// Airport code: YYZ
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// Airport name: London Heathrow
// Airport name: Toronto Pearson
如果需要将字典的键或值与接受 Array 实例的 API 一起使用,请使用 keys 或 values 属性初始化新数组:
let airportCodes = [String](airports.keys)
// airportCodes is ["LHR", "YYZ"]
let airportNames = [String](airports.values)
// airportNames is ["London Heathrow", "Toronto Pearson"]
Swift 的 Dictionary 类型没有定义的顺序。要按特定顺序遍历字典的键或值,请对其 keys 或 values 属性使用 sorted() 方法。