Swift Learning (6) - Collection Types (Fully Ver.)

Swift Learning (6) - Collection Types (Fully Ver.)

August 20, 2021·Jensen
Jensen

image

Swift语言提供数组(Array)、集合(Set)和字典(Dictionary)三种基本的集合类型来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集

Collection Type Intro

Swift中的数组、集合和字典必须明确其中保存的键和值类型,这样就可以避免插入一个错误数据类型的值。同理,对于获取到的值也可以放心,其数据类型是确定的。

注意

Swift数组、集合和字典类型被实现为泛型集合。


集合的可变性

如果创建一个数组、集合或字典并且把它分配成一个变量,这个集合将会是可变的。这意味着可以在创建之后添加、修改或者删除数据项。如果把数组、集合或者字典分配成常量,那么其就是不可变的,它的大小和内容都不能被改变。

注意

在不需要改变集合的时候创建不可变集合是很好的实践。这样做便于开发者理解自己的代码,也能让Swift编译器优化集合的性能。


数组(Arrays)

数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。

注意

Swift的Array类型被桥接FoundationNSArray类。

数组的简单语法

Swift中数组的完整写法Array<Element>,其中Element是这个数组中唯一允许存在的数据类型。也可以使用像[Element]这样简单的语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在文本中都会使用这种形式来创建数组。

var initArray: Array<String> = Array<String>()
initArray.append("hello")
print("类型Array<String>的数组为:\(initArray)")
---
output: 类型Array<String>的数组为:["hello"]

创建一个空数组

可以使用构造语法来创建一个由特定数据类型构成的空数组。

var someInts: [Int] = []
print("someInts is of type [Int] with \(someInts.count) items.")  // 打印“someInts is of type [Int] with 0 items.”
---
output: someInts is of type [Int] with 0 items.

注意,通过构造函数的类型,someInts的值类型被推断为[Int]

或者,如果代码上下文中已经提供了类型信息,例如一个函数参数或者一个已经定义好类型的常量或者变量,可以使用空数组语句创建一个空数组,它的写法很简单:[](一对空方括号):

someInts.append(3)
print("现在的someInts是:\(someInts)。")
someInts = []
print("someInts现在是\(someInts),但是依然是[Int]类型的。")
---
output: 现在的someInts是:[3]
someInts现在是[],但是依然是[Int]类型的。

创建一个带有默认值的数组

Swift中的Array类型还提供一个可以创建特定大小且所有数据都被默认的构造方法。可以把准备加入新数组的数据项数量(count)和适当类型的初始值(repeating)传入数组构造函数。

var threeDoubles = Array(repeating: 0.0, count: 3)  // threeDoubles是一种[Double]数组,等价于[0.0, 0.0, 0.0]
print("threeDoubles is \(threeDoubles)")
---
output: threeDoubles is [0.0, 0.0, 0.0]

通过两个数组相加创建一个数组

可以使用加法操作符+来组合两个已存在的相同类型的数组。新数组的数据类型会从两个数组的数据类型中推断出来。

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)  // anotherThreeDoubles被推断为[Double],等价于[2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles  // sixDoubles被推断为[Double],等价于[0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
print("sixDoubles的值为:\(sixDoubles)")
---
output: sixDoubles的值为:[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已经被构造且拥有两个初始项
print("shoppingList is \(shoppingList)")
---
output: shoppingList is ["Eggs", "Milk"]

shoppingList变量被声明为“字符串值类型的数组”,记作[String]。因为这个数组被规定只有String一种数据结构,所以只有String类型可以在其中被存取。在这里,shoppingList数组由两个String值(“Eggs”和“Milk”)构造,并由数组字面量定义。

注意

shoppingList数组被声明为变量(var关键字创建)而不是常量(let创建)是因为之后会有更多的数据项被插入其中。

在上述例子中,字面量仅仅包含两个String值。匹配了该数组的声明(只能包含String数组),所以可以将这个字面量的赋值过程看作用两个初始项来构造shoppingList的一种方式。

由于Swift的类型推断机制,当使用字面量构造拥有相同类型值数组的时候,不必把数组的类型定义清楚,shoppingList的构造也可以这样写:

var shoppingLists = ["Eggs", "Milk"]

因为所有的数组字面量中的值都是相同类型的,Swift可以推断出[String]shoppingList中变量的正确类型。

访问和修改数组

可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。

可以使用数组的只读属性count来获取数组中的数据项数量:

print("The shopping list contains \(shoppingLists.count) items.")
---
output: The shopping list contains 2 items.

使用布尔属性isEmpty作为一个缩写形式去检查count属性是否为0

if shoppingList.isEmpty {
    print("The shopping list is empty.")
} else {
    print("The shopping list is not empty.")
}
// 打印“The shopping list is not empty.”(shoppinglist不是空的)
---
output: The shopping list is not empty.

也可以使用append(_:)方法在数组后面添加新的数据项:

shoppingList.append("Flour")  // shoppingList现在有3个数据项,似乎有人在摊煎饼
print("添加‘Flour’选项后的shoppingList值为:\(shoppingList)。")
---
output: 添加‘Flour’选项后的shoppingList值为:["Eggs", "Milk", "Flour"]

除此之外,也可以使用加法赋值运算符+=直接将另一个相同类型数组中的数据添加到数组后面。

shoppingList += ["Baking Powder"]  // shoppingList现在有四项了
shoppingList += ["Chocolate Spread", "Chinese", "Butter"]  // shoppingList现在有七项了

可以直接使用下标语法来获取数组中的数据项,把所需要的数据项的索引值直接放在数组名称之后的方括号中:

var firstItem = shoppingList[0]
print("shoppingList的第一项是:\(firstItem)。")
---
output: shoppingList的第一项是:Eggs

注意

第一项在数组中的索引值是0而不是1。Swift中的数组索引总是从零开始。

也可以用下标来改变某个有效索引值对应的数据值:

shoppingList[0] = "Six Eggs"  // 现在shoppingList的第一项是“Six Eggs”而不是“Eggs”
print("修改后的shoppingList值为:\(shoppingList)")
---
output: 修改后的shoppingList值为:["Six Eggs", "Milk", "Flour", "Baking Powder", "Chocolate Spread", "Chinese", "Butter"]

当使用下标语法,所使用的下标必须是有效的。例如,试图通过shoppingList[shoppingList.count] = "Salt"在数组的最后添加一项,将产生一个运行时的错误。

还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的的。下面的例子把“Chocolate Spread”、“Chese”和“Butter”替换为“Bananas”和“Apples”。

shoppingList[4...6] = ["Bananas", "Apples"]  // shoppingList现在有6项
print("shoppingList现在是:\(shoppingList)")
// 通过调用数组的`insert(_:at:)`方法在某个指定索引值前面添加数据项:
shoppingList.insert("Maple Syrup", at: 0)  // shoppingList现在有7项
// 现在是这个列表中的第一项是“Maple Syrup”
print("shopingList现在的第一项是:\(shoppingList[0])")
// 这次`insert(_:at:)`方法调用把值为"Maple Syrup"的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
---
output: shoppingList现在是:["Six Eggs", "Milk", "Flour", "Baking Powder", "Bananas", "Apples"]
shopingList现在的第一项是:Maple Syrup

类似的可以使用remove(at:)方法来移除数组的某一项。这个方法吧数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(不需要时就可以无视它)。

let mapleSyrup = shoppingList.remove(at: 0)  // 现在shoppingList只有6项,不包括“Maple Syrup”,常量mapleSyrup等于“Maple Syrup”
print("mapleSyrup的值等于:\(mapleSyrup),shoppingList等于:\(shoppingList).")
---
output: mapleSyrup的值等于:Maple SyrupshoppingList等于:["Six Eggs", "Milk", "Flour", "Baking Powder", "Bananas", "Apples"].

注意

如果试图通过越界索引来执行访问或者修改数据的操作,会引发一个运行时的错误。此时可以使用索引值和数组的count属性进行比较来在使用该索引之前检验其是否有效。除了当count等于0时(说明这是个空数组),最大的索引一直是count - 1,因为数组都是零起索引。

数据项被移除后数组中的空出项会被自动填补,所以现在的索引值为0的数据项的值再次等于“Six eggs”。

firstItem = shoppingList[0]  // firstItem现在等于"Six eggs"
print("此时shoppingList首位是:\(firstItem)")
// 如果只想把数组中的最后一项移除,可以使用`removeList()`方法而不是`remove(at:)`方法来避免需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项。
let apples = shoppingList.removeLast()  // 数组的最后一项被移除了,shoppingList现在只有5项,不包括“Apples”,常量apples现在等于字符串“Apples”
print("常量apples现在的值:\(apples),shoppingList等于:\(shoppingList).")
// 同样地还有`removeFirst()`方法,直接移除数组的第一项。
let sixEggs = shoppingList.removeFirst()
---
output: 此时shoppingList首位是:Six Eggs
常量apples现在的值:ApplesshoppingList等于:["Six Eggs", "Milk", "Flour", "Baking Powder", "Bananas"].

数组的遍历

可以使用for-in循环来遍历数组中所有的数据项:

for item in shoppingList {
    print(item)  // Six eggs; Milk; Flour; Baking Powder; Bananas
}
---
output: Milk
Flour
Baking Powder
Bananas

如果同时需要每个数据项的值和索引值,可以使用“enumerated()”方法来进行数组遍历。enumerated()返回一个有索引值和数据值组成的元组数据。索引值从零开始,并且每次增加一;如果枚举一整个数组,索引值将会和数据值一一匹配。可以把这个元组分解成临时变量或者变量来进行遍历:

for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")  // Item 1: Milk; Item 2: Flour; Item 3: Baking Powder; Item 4: Bananas
}
---
output: Item 1: Milk
Item 2: Flour
Item 3: Baking Powder
Item 4: Bananas

集合(Sets)

集合用来存储相同类型ß但是没有确定顺序的值。当集合元素的顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。

注意

Swift中的Set类型被桥接到Foundation中的NSSet类。

集合类型的哈希值

一个类型为了存储在集合中,该类型必须是可哈希化的,也就是说,该类型必须提供一个方法来计算其哈希值。一个哈希值时Int类型的,相等的对象哈希值必须相同,比如a == b,因此必须a.hashValue == b.hashValue

Swift的所有基本类型(比如String、Int、Double和Bool)默认都是可哈希化的,可以作为集合值的类型或者字典键的类型。没有关联值的枚举成员值默认也是可以哈希化的。

注意

可以使用自定义的类型作为集合值的类型或者是字典键的类型,但需要使自定义的类型遵循Swift标准库中的Hashable协议。遵循Hashable协议的类型需要提供一个类型为Int的可读属性hashValue。由类型的hashValue属性返回的值不需要在同一程序的不同执行周期或者不同程序间保持相同。

因为Hashable遵循Equatable协议,所以遵循该协议的类型也必须提供一个“是否相等”运算符(==)的实现。这个Equatable协议要求任何遵循 == 实现的实例间都是一种相等的关系。也就是说,对于a,b,c三个值来说,==的实现必须满足下面三种情况:

  • a == a(自反性)
  • a == b 意味着 b == a(对称性)
  • a == b && b == c 意味着 a == c(传递性)

集合类型语法

Swift中的集合类型被写为Set<Element>,这里的Element表示集合中允许存储的类型。和数组不同的是,集合没有等价的简化形式。

创造和构建一个空的集合

可以通过构造器语法创建一个特定类型的空集合

var letters = Set<Character>()
print("Letters is of type Set<Character> with \(letters.count) items.")  // 打印输出“Letters is of type Set<Character> with 0 items.”
---
output: Letters is of type Set<Character> with 0 items.

注意

通过构造器,这里的letters变量的类型被推断为Set

此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,可以通过一个空的数组字面量创建一个空的集合。

letters.insert("A")  // letters现在含有1个Character类型的值
print("现在Letters值为:\(letters)")
letters = []  // letters现在是一个空的Set,但是它依然是Set<Character>类型
print("此时Letters值为:\(letters)")
---
output: 现在Letters值为:["A"]
此时Letters值为:[]

用数组字面量创建集合

可以使用数组字面量来构造集合,相当于一种简化的形式将一个或多个值作为集合元素。

下面的例子创建一个称之为favoriteGenres的集合来存储String类型的值。

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]  // favoriteGenres被构造成含有三个初始值的集合

这个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"]

由于数组字面量中的所有元素类型相同均为“String”,Swift可以推断出Set<String>作为favoriteGenres变量的正确类型。

访问和修改一个集合

可以通过集合的属性和方法来对其进行访问和修改

为了获取一个集合中元素的数量,可以使用其只读属性count

print("I have \(FavoriteGenres.count) favorite music genres.")  // 打印“I have 3 favorite music genres.”
---
output: 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.")
}  // 打印“I have particular music preferences.”
---
output: I have particular music preferences.

可以通过调用集合的insert(_:)方法来添加一个新元素:

FavoriteGenres.insert("Jazz")  // FavoriteGenres现在包含4个元素。
print("FavoriteGenres现在包含\(FavoriteGenres.count)个元素,其值为:\(FavoriteGenres)")
---
output: FavoriteGenres现在包含4个元素,其值为:["Classical", "Jazz", "Hip hop", "Rock"]

可以通过调用集合的remove(_:)方法去删除一个元素,如果它是该集合的一个元素则删除它并且返回它的值,若该集合不包含它,则返回nil。另外,集合可以通过removeAll()方法删除所有元素。

if let removeGenre = FavoriteGenres.remove("Rock") {
    print("\(removeGenre)? I'm over it!")
} else {
    print("I never much cared of that!")
}  // 打印“Rock? I'm over it!”
---
output: 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.")
}  // 打印"It's too funky in here."
---
output: It's too funky in here.

遍历一个集合

可以在一个for-in循环中遍历一个集合中的所有值。

for genre in FavoriteGenres {
    print("\(genre)")
}  // Hip hop; // Classical; // Jazz  无序排列
---
output: Classical
Jazz
Hip hop

SwiftSet类型没有确定的顺序,为了按照特定顺序来遍历一个集合中的值可以使用sorted()方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符<对元素进行比较的结果来确定。

for genre in FavoriteGenres.sorted() {
    print("\(genre)")
}  // Classical; // Hip hop; // Jazz  有序排列
---
output: Classical
Hip hop
Jazz

集合操作

可以高效地完成集合的一些基本操作,比如把两个集合组合到一起,判断两个集合的共有元素,或者判断两个集合是否全包含,部分包含或者不相交。

基本集合操作

SetVennDiagram

  • 使用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]

print("Union: \(oddDigits.union(evenDigits).sorted())")  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print("InterSection: \(oddDigits.intersection(evenDigits).sorted())")  // []
print("SubTracting: \(oddDigits.subtracting(singleDigitPrimeNumbers).sorted())")  // [1, 9]
print("SymmetricDifference: \(oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted())")  // [1, 2, 9]
---
output: Union: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
InterSection: []
SubTracting: [1, 9]
SymmetricDifference: [1, 2, 9]

集合成员关系和相等

SetEulerDiagram

  • 使用“是否相等”运算符==来判断两个集合包含的值是否相同。
  • 使用isSubset(of:)方法来判断一个集合中的所有值是否也被包含在另外一个集合中。
  • 使用isSpuerset(of:)方法来判断一个集合是否包含另一个集合中的所有值。
  • 使用isScrictSubset(of:)或者isScrictSuperset(of:)方法来判断一个集合是否是另外一个集合的子集或者父集合并且两个集合不相等。
  • 使用isDisjoint(with:)方法来判断两个集合是否不含有相同的值(是否没有交集)。
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]

print("IsSubset? \(houseAnimals.isSubset(of: farmAnimals))")  // 打印输出"IsSubset? true"
print("IsSuperset? \(farmAnimals.isSuperset(of: houseAnimals))")  // 打印输出"IsSuperset? true"
print("IsDisjoint? \(farmAnimals.isDisjoint(with: cityAnimals))")  // 打印输出"IsDisjoint? true"
---
output: IsSubset? true
IsSuperset? true
IsDisjoint? true

字典

字典是一种无序的集合,它存储的是键值对之间的关系,其所有的值需要是相同的类型,所有值的类型也需要相同。每个值(value)都关联唯一的键(key),键作为字典中这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体的顺序。在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和现实世界中使用字典查字义的方法一样。

注意

Swift的Dictionary类型被桥接到Foundation的NSDictionary类。

字典类型简化语法

Swift的字典使用Dictionary<Key, Value>定义,其中Key是一种可以在字典中被用作键的类型,Value是字典中对应于这些键所存储值的数据类型。

注意

一个字典的Key类型必须遵循Hashable协议,就像Set的值类型。

也可以使用[Key: Value]这样简化的形式去表示字典类型。虽然这两种形式功能上相同,但是后者是首选,并且本教程中涉及到字典类型时通篇采用后者。

创建一个空字典

可以像数组一样使用构造语法创建一个拥有确定类型的空字典:

var namesOfInteger: Dictionary<Int, String> = Dictionary<Int, String>()  // 使用Dictionary<Key, Value>定义字典
print("使用Dictionary<Key, Value>定义字典:\(namesOfInteger)")
var namesOfIntegers: [Int: String] = [:]  // 简化语法,首选!
print("简化语法,首选:\(namesOfIntegers)")
---
output: 使用Dictionary<Key, Value>定义字典:[:]
简化语法,首选:[:]

这个例子创建了一个[Int: String]类型的空字典来存储整数的英语命名。它的键是Int型,值是String型。

如果上下文已经提供了类型信息,可以使用空字典字面量来创建一个空字典,记作[:](一对方括号中放一个冒号):

namesOfIntegers[16] = "sixteen"  // namesOfIntegers现在包含一个键值对
namesOfIntegers = [:]  // namesOfIntegers又成为了一个[Int: String]类型的空字典

用字典字面量创建字典

可以使用字典字面量来构造字典,这和刚才介绍过的数组字面量拥有相似的语法。字典字面量是一种将一个或多个键值对写作Dictionary集合的快捷途径。

一个键值对是一个键和一个值的结合体,在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由逗号分割、并整体被包裹在一对方括号中:

// [key 1: value 1, key 2: value 2, key 3: value 3]

下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:

var airposts: [String: String] = ["HFE": "Hefei Xinqiao", "NKG": "Nanjing Lukou", "PKX":"Beijing Daxing", "SHA":"Shanghao Hongqiao"]
print("China Main Airpots: \(airposts)")
---
output: China Main Airpots: ["HFE": "Hefei Xinqiao", "NKG": "Nanjing Lukou", "PKX": "Beijing Daxing", "SHA": "Shanghao Hongqiao"]

airports字典被声明为一种[String: String]类型,这意味着这个字典的键和值都是String类型。

注意

airports字典被声明为变量(用var关键字)而不是常量(用let关键字)因为后面会有更多的机场被添加到这个字典中。

airports字典使用字典字面量初始化,包含四个键值对。它们对应airports变量声明的类型(一个只有String键和String值的字典),所以这个字典字面量的赋值是一种方式用来构造拥有四个初始数据项的airports字典。

和数组一样,在用字典字面量构造字典时,如果其键和值都有各自一致的类型,那就不必写出字典的类型,airports字典也可以用这种简短的方式定义:

var chinaAirports = ["HFE": "Hefei Luogang", "NKG": "Nanjing Lukou", "PKX":"Beijing Daxing", "SHA":"Shanghao Hongqiao"]

因为这个语句中所有的键值都各自拥有相同的数据类型,Swift可以推断出[String: String]airports字典的正确类型。

访问和修改字典

可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。

和数组一样,可以通过Dictionary的只读属性count来获取字典的数据项数量:

print("The dictionary of chinaAirports contains \(chinaAirports.count) items.")  // 打印"The dictionary of chinaAirports contains 4 items."
---
output: The dictionary of chinaAirports contains 4 items.

使用布尔属性isEmpty作为一个缩写去检查count属性是否为0:

if chinaAirports.isEmpty {
    print("The chinaAirports dictionary is empty.")
} else {
    print("The chinaAirports dictionary is not empty.")
}  // 打印"The chinaAirports dictionary is not empty."
---
output: The chinaAirports dictionary is not empty.

可以通过下标语法来个字典添加新的数据项,可以使用一个恰当类型的键作为下标索引,并且分配恰当类型的新值:

airposts["XIY"] = "Xi'an"  // airports字典现在有五个数据项
print(airposts)
---
output: ["HFE": "Hefei Xinqiao", "NKG": "Nanjing Lukou", "PKX": "Beijing Daxing", "XIY": "Xi\'an", "SHA": "Shanghao Hongqiao"]

也可以使用下标语法来改变特定键对应的值:

airposts["XIY"] = "Xi'an Xianyang"  // "XIY"对应的值被修改为"Xi'an Xianyang"
print(airposts)
---
output: ["HFE": "Hefei Xinqiao", "NKG": "Nanjing Lukou", "PKX": "Beijing Daxing", "XIY": "Xi\'an Xianyang", "SHA": "Shanghao Hongqiao"]

作为一种替代下标语法的方式,字典的updateValue(_:forKey:)方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,updateValue(_:forKey:)方法在这个键不存在对应值的时候会设置新值或者在存在时更新已存在的值。和下标的方式不同,updateValue(_:forKey:)这个方法返回更新值之前的原值。这样使得可以检查更新是否成功。

updateValue(_:forKey:)方法会返回对应值类型的可选类型。举例来说:对于存储String值的字典,这个函数会返回一个String?或者可选String类型的值。如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是nil

if let oldValue = airposts.updateValue("Hefei Xinqiao", forKey: "HFE") {
    print("The old value for HFE was \(oldValue).")
}  // 打印输出"The old value for HFE was Hefei Xinqiao."
---
output: The old value for HFE was Hefei Xinqiao.

也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回对应值的可选类型。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选类型,否则将返回nil:

if let airportName = airposts["HFE"] {
    print("The name of the airport is \(airportName).")
} else {
    print("That airport is not in the airports dictionary.")
}  // 打印"The name of the airport is Hefei Xinqiao."
---
output: The name of the airport is Hefei Xinqiao.

还可以使用下标语法通过将某个键的对应值赋值为nil来从字典中移除一个键值对:

airposts["CAN"] = "Guangzhou Baoan"  // Baoan机场是深圳的,所以删除它
print(airposts)
airposts["CAN"] = nil  // CAN现在被移除了
print(airposts)
---
output: ["HFE": "Hefei Xinqiao", "NKG": "Nanjing Lukou", "PKX": "Beijing Daxing", "XIY": "Xi\'an Xianyang", "CAN": "Guangzhou Baoan", "SHA": "Shanghao Hongqiao"]
["HFE": "Hefei Xinqiao", "NKG": "Nanjing Lukou", "PKX": "Beijing Daxing", "XIY": "Xi\'an Xianyang", "SHA": "Shanghao Hongqiao"]

此外,removeValue(forKey:)方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有对应值的情况下返回nil

if let removeValue = airposts.removeValue(forKey: "NKG") {
    print("The removed airport's name is \(removeValue).")
} else {
    print("The airports dictionary does not contain a value for NKG.")
}  // 打印"The removed airport's name is Nanjing Lukou."
---
output: The removed airport's name is Nanjing Lukou.

字典遍历

可以使用for-in循环来遍历某个字典中的键值对。每一个字典中的数据项都以(Key, Value)元组形式返回,并且可以使用临时常量或者变量来分解这些元组。

for (airportCode, airportName) in airposts {
    print("\(airportCode): \(airportName)")
}  // HFE: Hefei Xinqiao; // PKX: Beijing Daxing; //SHA: Shanghao Hongqiao; // XIY: Xi'an Xianyang
---
output: HFE: Hefei Xinqiao
PKX: Beijing Daxing
XIY: Xi'an Xianyang
SHA: Shanghao Hongqiao

通过访问keys或者values属性,可以遍历字典的键或值:

for airportCode in airposts.keys {
    print("Airport code: \(airportCode)")
}

for airportName in airposts.values {
    print("Airport name: \(airportName)")
}
---
output: Airport code: HFE
Airport code: PKX
Airport code: XIY
Airport code: SHA
Airport name: Hefei Xinqiao
Airport name: Beijing Daxing
Airport name: Xi'an Xianyang
Airport name: Shanghao Hongqiao

如果需要使用某个字典的键集合或者值集合来作为某个接受Array实例的API的参数,可以直接使用keys或者values属性来构造一个新数组。

let airportCodes = [String](airposts.keys)  // airportCodes是["HFE", "PKX", "SHA", "XIY"]
print(airportCodes)
let airportNames = [String](airposts.values)  // airportNames是["Hefei Xinqiao", "Beijing Daxing", "Shanghai Hongqiao", "Xi'an Xianyang"]
print(airportNames)
---
output: ["HFE", "PKX", "XIY", "SHA"]
["Hefei Xinqiao", "Beijing Daxing", "Xi\'an Xianyang", "Shanghao Hongqiao"]

SwiftDictionary是无序集合类型。为了以特定顺序遍历字典的键或值,可以对字典的keysvalues属性使用sorted()方法。

Last updated on