Swift提供了三种主要的集合类型,称为数组,集合和字典,用于存储值集合。数组是有序的值集合。集是唯一值的无序集合。字典是键值关联的无序集合。
Swift中的数组,集合和字典总是清楚它们可以存储的值和键的类型。这意味着您不能错误地将错误类型的值插入到集合中。这也意味着您可以对从集合中检索的值的类型充满信心。
注意
Swift的数组,集合和字典类型实现为泛型集合。有关泛型类型和集合的更多信息,请参阅泛型。
集合的可变性
如果创建数组,集合或字典,并将其分配给变量,则创建的集合将是可变的。这意味着,你可以改变(或变异它是由添加,删除或改变集合中的项目创建后)的集合。如果将数组,集合或字典分配给常量,则该集合是不可变的,并且其大小和内容不能更改。
注意
在集合不需要更改的所有情况下,最好创建不可变集合。这样做可以使您更容易推理代码并使Swift编译器能够优化您创建的集合的性能。
数组
一个阵列存储在有序列表中的相同类型的值。相同的值可以在不同位置多次出现在数组中。
注意
Swift的Array
类型被桥接到Foundation的NSArray
类。
有关Array
与Foundation和Cocoa一起使用的更多信息,请参阅数组和NSArray之间的桥接。
数组类型简单语法
Swift数组的类型是完整写入的Array<Element>
,其中Element
允许数组存储的值的类型。您也可以用简写形式编写数组类型[Element]
。尽管这两种形式在功能上是相同的,但是简写形式是优选的,并且在引用阵列的类型时在本指南中使用。
创建一个空数组
您可以使用初始化语法创建某个类型的空数组:
1 var someInts = [Int]() 2 print("someInts is of type [Int] with \(someInts.count) items.") 3 // Prints "someInts is of type [Int] with 0 items."
请注意,someInts
变量的类型推断为[Int]
来自初始化程序的类型。
或者,如果上下文已经提供了类型信息,例如函数参数或已经键入的变量或常量,则可以使用空数组文字创建一个空数组,该数组写为[]
(一对空方括号):
1 someInts.append(3) 2 // someInts now contains 1 value of type Int 3 someInts = [] 4 // someInts is now an empty array, but is still of type [Int]
创建具有默认值的数组
Swift的Array
类型还提供了一个初始化器,用于创建一个特定大小的数组,并将其所有值设置为相同的默认值。您将此初始值设定项传递给相应类型(称为repeating
)的默认值:以及在新数组(被调用count
)中重复该值的次数:
1 var threeDoubles = Array(repeating: 0.0, count: 3) 2 // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
通过添加两个数组来创建数组
您可以通过使用加法运算符(+
)将两个具有兼容类型的现有数组相加来创建新数组。新数组的类型是从您添加的两个数组的类型推断出来的:
1 var anotherThreeDoubles = Array(repeating: 2.5, count: 3) 2 // anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5] 3 4 var sixDoubles = threeDoubles + anotherThreeDoubles 5 // sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
使用Array Literal创建数组
您还可以使用数组文字初始化数组,这是将一个或多个值写为数组集合的简便方法。数组文字被写为值列表,以逗号分隔,由一对方括号括起:
[value 1, value 2, value 3]
下面的示例创建一个名为shoppingList
存储String
值的数组:
1 var shoppingList: [String] = ["Eggs", "Milk"] 2 // 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
属性:
1 print("The shopping list contains \(shoppingList.count) items.") 2 // Prints "The shopping list contains 2 items."
使用Boolean isEmpty
属性作为检查count
属性是否等于的快捷方式0
:
1 if shoppingList.isEmpty { 2 print("The shopping list is empty.") 3 } else { 4 print("The shopping list is not empty.") 5 } 6 // Prints "The shopping list is not empty."
您可以通过调用数组的append(_:)
方法将新项添加到数组的末尾:
1 shoppingList.append("Flour") 2 // shoppingList now contains 3 items, and someone is making pancakes
或者,使用添加赋值运算符(+=
)追加一个或多个兼容项的数组:
1 shoppingList += ["Baking Powder"] 2 // shoppingList now contains 4 items 3 shoppingList += ["Chocolate Spread", "Cheese", "Butter"] 4 // shoppingList now contains 7 items
使用下标语法从数组中检索值,在数组名称后面的方括号内传递要检索的值的索引:
1 var firstItem = shoppingList[0] 2 // firstItem is equal to "Eggs"
注意
数组中的第一项具有索引0
,而不是1
。Swift中的数组始终为零索引。
您可以使用下标语法来更改给定索引处的现有值:
1 shoppingList[0] = "Six eggs" 2 // 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"
1 shoppingList[4...6] = ["Bananas", "Apples"] 2 // shoppingList now contains 6 items
要在指定索引处将项插入数组,请调用数组的insert(_:at:)
方法:
1 shoppingList.insert("Maple Syrup", at: 0) 2 // shoppingList now contains 7 items 3 // "Maple Syrup" is now the first item in the list
此insert(_:at:)
方法调用会在购物清单的最开头插入一个新项目,其值由索引表示。"Maple Syrup"
0
同样,您使用该remove(at:)
方法从数组中删除项。此方法删除指定索引处的项目并返回已删除的项目(尽管如果不需要,可以忽略返回的值):
1 let mapleSyrup = shoppingList.remove(at: 0) 2 // the item that was at index 0 has just been removed 3 // shoppingList now contains 6 items, and no Maple Syrup 4 // the mapleSyrup constant is now equal to the removed "Maple Syrup" string
注意
如果尝试访问或修改数组现有边界之外的索引的值,则会触发运行时错误。通过将索引与数组的count
属性进行比较,可以在使用之前检查索引是否有效。数组中最大的有效索引是因为数组从零开始索引 - 但是,当is (意味着数组为空)时,没有有效的索引。count - 1
count
0
删除项时,数组中的任何间隙都会关闭,因此index 0
处的值再次等于:"Six eggs"
1 firstItem = shoppingList[0] 2 // firstItem is now equal to "Six eggs"
如果要从数组中删除最终项,请使用removeLast()
方法而不是remove(at:)
方法,以避免查询数组的count
属性。与remove(at:)
方法一样,removeLast()
返回已删除的项:
1 let apples = shoppingList.removeLast() 2 // the last item in the array has just been removed 3 // shoppingList now contains 5 items, and no apples 4 // the apples constant is now equal to the removed "Apples" string
迭代数组
可以遍历整个集合值与数组for
- in
循环:
1 for item in shoppingList { 2 print(item) 3 } 4 // Six eggs 5 // Milk 6 // Flour 7 // Baking Powder 8 // Bananas
如果需要每个项的整数索引及其值,请使用该enumerated()
方法迭代数组。对于数组中的每个项,该enumerated()
方法返回由整数和项组成的元组。整数从零开始,每个项目计数加1; 如果枚举整个数组,则这些整数与项目的索引匹配。作为迭代的一部分,您可以将元组分解为临时常量或变量:
1 for (index, value) in shoppingList.enumerated() { 2 print("Item \(index + 1): \(value)") 3 } 4 // Item 1: Six eggs 5 // Item 2: Milk 6 // Item 3: Flour 7 // Item 4: Baking Powder 8 // Item 5: Bananas
有关for
- in
循环的更多信息,请参阅For-In循环。
集合
集合在集合中存储相同类型的不同值,没有定义的顺序。当项目的顺序不重要时,或者当您需要确保项目仅出现一次时,您可以使用集合而不是数组。
注意
Swift的Set
类型被桥接到Foundation的NSSet
c类。
有关Set
与Foundation和Cocoa一起使用的更多信息,请参阅Set和NSSet之间的桥接。
集合类型的哈希值
类型必须是可散列的才能存储在集合中 - 即,类型必须提供计算自身散列值的方法。哈希值是Int
对于同等比较的所有对象的相同值,如果是,则遵循该值。a == b
a.hashValue == b.hashValue
所有Swift的基本类型(例如String
,Int
,Double
,和Bool
)默认情况下可哈希,并可以作为设定值类型或字典密钥类型。默认情况下,没有关联值的枚举大小写值(如枚举中所述)也是可以清除的。
注意
您可以使用自己的自定义类型作为设置值类型或字典键类型,使其符合Hashable
Swift标准库中的协议。符合Hashable
协议的类型必须提供一个Int
名为gettable的属性hashValue
。类型hashValue
属性返回的值在同一程序的不同执行或不同程序中不需要相同。
由于Hashable
协议符合Equatable
,符合类型还必须提供equals operator(==
)的实现。该Equatable
协议要求任何符合要求的实现==
是等价关系。也就是说,一个实现==
必须满足以下三个条件,所有值a
,b
以及c
:
a == a (自反) a == b 暗指(对称性)b == a a == b && b == c 暗指(传递性)a == c
有关符合协议的更多信息,请参阅协议。
设置类型语法
Swift集的类型写为Set<Element>
,Element
允许集存储的类型在哪里。与数组不同,集合没有等效的简写形式。
创建和初始化空集
您可以使用初始化语法创建某个类型的空集:
1 var letters = Set<Character>() 2 print("letters is of type Set<Character> with \(letters.count) items.") 3 // Prints "letters is of type Set<Character> with 0 items."
注意
根据初始化程序的类型letters
推断变量Set<Character>
的类型。
或者,如果上下文已经提供了类型信息,例如函数参数或已经键入的变量或常量,则可以使用空数组文字创建一个空集:
1 letters.insert("a") 2 // letters now contains 1 value of type Character 3 letters = [] 4 // letters is now an empty set, but is still of type Set<Character>
使用Array Literal创建集合
您还可以使用数组文字初始化集合,作为将一个或多个值写为集合集合的简写方式。
下面的示例创建一个名为favoriteGenres
存储String
值的集合:
1 var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"] 2 // 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
属性:
1 print("I have \(favoriteGenres.count) favorite music genres.") 2 // Prints "I have 3 favorite music genres."
使用Boolean isEmpty
属性作为检查count
属性是否等于的快捷方式0
:
1 if favoriteGenres.isEmpty { 2 print("As far as music goes, I'm not picky.") 3 } else { 4 print("I have particular music preferences.") 5 } 6 // Prints "I have particular music preferences."
您可以通过调用set的insert(_:)
方法将新项添加到集合中:
1 favoriteGenres.insert("Jazz") 2 // favoriteGenres now contains 4 items
您可以通过调用set的remove(_:)
方法从集合中删除项目,如果该项目是集合的成员,则删除该项目,并返回已删除的值,nil
如果该集合不包含该项目,则返回该项目。或者,可以使用其removeAll()
方法删除集合中的所有项目。
1 if let removedGenre = favoriteGenres.remove("Rock") { 2 print("\(removedGenre)? I'm over it.") 3 } else { 4 print("I never much cared for that.") 5 } 6 // Prints "Rock? I'm over it."
要检查集合是否包含特定项目,请使用该contains(_:)
方法。
1 if favoriteGenres.contains("Funk") { 2 print("I get up on the good foot.") 3 } else { 4 print("It's too funky in here.") 5 } 6 // Prints "It's too funky in here."
迭代集合
您可以使用for
- in
循环遍历集合中的值。
1 for genre in favoriteGenres { 2 print("\(genre)") 3 } 4 // Classical 5 // Jazz 6 // Hip hop
有关for
- in
循环的更多信息,请参阅For-In循环。
Swift的Set
类型没有定义的顺序。要以特定顺序迭代集合的值,请使用该sorted()
方法,该方法将集合的元素作为使用<
运算符排序的数组返回。
1 for genre in favoriteGenres.sorted() { 2 print("\(genre)") 3 } 4 // Classical 5 // Hip hop 6 // Jazz
执行集合操作
您可以有效地执行基本集合操作,例如将两个集合在一起,确定两个集合具有哪些值,或者确定两个集合是否包含全部,一些或不包含相同的值。
基本集合运算
下图描绘了两个集- a
和b
-附由阴影区域表示的各种设定操作的结果。
-
使用该
intersection(_:)
方法创建一个只包含两个组共有的值的新集。 -
使用该
symmetricDifference(_:)
方法创建一个新集合,其中包含任一集合中的值,但不能同时创建两者。 -
使用该
union(_:)
方法创建包含两个集中所有值的新集。 -
使用此
subtracting(_:)
方法创建一个值不在指定集中的新集。
1 let oddDigits: Set = [1, 3, 5, 7, 9] 2 let evenDigits: Set = [0, 2, 4, 6, 8] 3 let singleDigitPrimeNumbers: Set = [2, 3, 5, 7] 4 5 oddDigits.union(evenDigits).sorted() 6 // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 7 oddDigits.intersection(evenDigits).sorted() 8 // [] 9 oddDigits.subtracting(singleDigitPrimeNumbers).sorted() 10 // [1, 9] 11 oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() 12 // [1, 2, 9]
设置成员和相等
下图描绘了三组a
,b
并且c
- 具有表示在组之间共享的元素的重叠区域。Set a
是set 的超集b
,因为a
包含了所有元素b
。相反,set b
是set 的子集a
,因为其中的所有元素b
也包含在内a
。设置b
和设置c
彼此不相交,因为它们没有共同的元素。
-
使用“is equal”运算符(
==
)来确定两个集合是否包含所有相同的值。 -
使用此
isSubset(of:)
方法确定集合的所有值是否都包含在指定的集合中。 -
使用此
isSuperset(of:)
方法确定集合是否包含指定集合中的所有值。 -
使用
isStrictSubset(of:)
或isStrictSuperset(of:)
方法确定集合是否是指定集合的子集或超集,但不等于。 -
使用该
isDisjoint(with:)
方法确定两个集合是否没有共同的值。
1 let houseAnimals: Set = ["????", "????"] 2 let farmAnimals: Set = ["????", "????", "????", "????", "????"] 3 let cityAnimals: Set = ["????", "????"] 4 5 houseAnimals.isSubset(of: farmAnimals) 6 // true 7 farmAnimals.isSuperset(of: houseAnimals) 8 // true 9 farmAnimals.isDisjoint(with: cityAnimals) 10 // true
字典
甲字典存储相同类型的密钥和一个集合中的相同类型的值与没有定义排序之间的关联。每个值都与唯一键相关联,该唯一键充当字典中该值的标识符。与数组中的项目不同,字典中的项目没有指定的顺序。当您需要根据标识符查找值时,可以使用字典,这与使用真实字典查找特定单词的定义的方式非常相似。
注意
Swift的Dictionary
类型被桥接到Foundation的NSDictionary
类。
有关Dictionary
与Foundation和Cocoa一起使用的更多信息,请参阅字典和NSDictionary之间的桥接。
字典类型简单语法
Swift字典的类型是完整的,因为可以用作字典键的值的类型,以及字典为这些键存储的值的类型。Dictionary<Key, Value>
Key
Value
注意
字典Key
类型必须符合Hashable
协议,如集合的值类型。
您也可以用简写形式编写字典类型。尽管这两种形式在功能上是相同的,但是简写形式是优选的,并且在引用字典的类型时在本指南中使用。[Key: Value]
创建一个空字典
与数组一样,您可以Dictionary
使用初始化程序语法创建某个类型的空:
1 var namesOfIntegers = [Int: String]() 2 // namesOfIntegers is an empty [Int: String] dictionary
此示例创建一个类型的空字典,以存储整数值的可读名称。它的键是类型,其值是类型。[Int: String]
Int
String
如果上下文已经提供了类型信息,您可以创建一个空字典,其中包含一个空的字典文字,其写为[:]
(一对方括号内的冒号):
1 namesOfIntegers[16] = "sixteen" 2 // namesOfIntegers now contains 1 key-value pair 3 namesOfIntegers = [:] 4 // namesOfIntegers is once again an empty dictionary of type [Int: String]
使用Dictionary Literal创建字典
您还可以使用字典文字初始化字典,字典文字与前面看到的数组文字的语法类似。字典文字是将一个或多个键值对编写为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]
Dictionary
String
String
注意
该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
访问和修改字典
您可以通过其方法和属性或使用下标语法来访问和修改字典。
与数组一样,您可以Dictionary
通过检查其只读count
属性来查找a中的项目数:
1 print("The airports dictionary contains \(airports.count) items.") 2 // Prints "The airports dictionary contains 2 items."
使用Boolean isEmpty
属性作为检查count
属性是否等于的快捷方式0
:
1 if airports.isEmpty { 2 print("The airports dictionary is empty.") 3 } else { 4 print("The airports dictionary is not empty.") 5 } 6 // Prints "The airports dictionary is not empty."
您可以使用下标语法将新项添加到字典中。使用适当类型的新键作为下标索引,并指定适当类型的新值:
1 airports["LHR"] = "London" 2 // the airports dictionary now contains 3 items
您还可以使用下标语法来更改与特定键关联的值:
1 airports["LHR"] = "London Heathrow" 2 // the value for "LHR" has been changed to "London Heathrow"
作为下标的替代updateValue(_:forKey:)
方法,使用字典的方法来设置或更新特定键的值。与上面的下标示例一样,updateValue(_:forKey:)
如果不存在,则该方法为键设置值,或者如果该键已存在则更新该值。但是,与下标不同,该updateValue(_:forKey:)
方法在执行更新后返回旧值。这使您可以检查是否发生了更新。
该updateValue(_:forKey:)
方法返回字典值类型的可选值。例如,对于存储String
值的字典,该方法返回type String?
或“optional String
”的值。如果在更新之前存在该键,或者nil
如果不存在任何值,则此可选值包含该键的旧值:
1 if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") { 2 print("The old value for DUB was \(oldValue).") 3 } 4 // Prints "The old value for DUB was Dublin."
您还可以使用下标语法从字典中检索特定键的值。因为可以请求不存在值的键,所以字典的下标返回字典值类型的可选值。如果字典包含所请求键的值,则下标返回包含该键的现有值的可选值。否则,下标返回nil
:
1 if let airportName = airports["DUB"] { 2 print("The name of the airport is \(airportName).") 3 } else { 4 print("That airport is not in the airports dictionary.") 5 } 6 // Prints "The name of the airport is Dublin Airport."
您可以使用下标语法通过nil
为该键指定值来从字典中删除键值对:
1 airports["APL"] = "Apple International" 2 // "Apple International" is not the real airport for APL, so delete it 3 airports["APL"] = nil 4 // APL has now been removed from the dictionary
或者,使用该removeValue(forKey:)
方法从字典中删除键值对。此方法删除键值对(如果存在)并返回已删除的值,nil
如果不存在值则返回:
1 if let removedValue = airports.removeValue(forKey: "DUB") { 2 print("The removed airport's name is \(removedValue).") 3 } else { 4 print("The airports dictionary does not contain a value for DUB.") 5 } 6 // Prints "The removed airport's name is Dublin Airport."
迭代字典
您可以用字典遍历键值对for
- in
环。字典中的每个项都作为元组返回,您可以将元组的成员分解为临时常量或变量,作为迭代的一部分:(key, value)
1 for (airportCode, airportName) in airports { 2 print("\(airportCode): \(airportName)") 3 } 4 // YYZ: Toronto Pearson 5 // LHR: London Heathrow
有关for
- in
循环的更多信息,请参阅For-In循环。
您还可以通过访问字典的键keys
和values
属性来检索字典的键或值的可迭代集合:
1 for airportCode in airports.keys { 2 print("Airport code: \(airportCode)") 3 } 4 // Airport code: YYZ 5 // Airport code: LHR 6 7 for airportName in airports.values { 8 print("Airport name: \(airportName)") 9 } 10 // Airport name: Toronto Pearson 11 // Airport name: London Heathrow
如果您需要使用带有Array
实例的API的字典键或值,请使用keys
或values
属性初始化新数组:
1 let airportCodes = [String](airports.keys) 2 // airportCodes is ["YYZ", "LHR"] 3 4 let airportNames = [String](airports.values) 5 // airportNames is ["Toronto Pearson", "London Heathrow"]
Swift的Dictionary
类型没有定义的顺序。要按特定顺序迭代字典的键或值,请sorted()
在其keys
或values
属性上使用该方法。