Swift learning part 4 - 集合类型

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

Swift 语言中的 Arrays、Sets 和 Dictionaries 中存储的数据值类型必须明确。这意味着我们不能把不正确的数据类型插入其中,同时也说明我们完全可以对取回值的类型非常自信。

集合的可变性

如果我们创建一个 Arrays、Sets 或 Dictionaries 并且把它分配成一个变量,这个集合将会是可变的,可以在创建之后添加更多或者删除已存在的数据项,或者修改集合中的数据项。如果把它被分配成一个常量,那么它就是不可变的,它的大小和内容都是不能改变的。

数组(Arrays)

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

注意:Swift 的 Array 类型被桥接到 Foundation 中的 NSArray 类。

数组的简单语法

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

创建一个空数组

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

var someInts = [Int]() // 通过构造函数的类型,someInts 的值类型被推断为 [Int]
print(someInts)

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

someInts.append(3) // someInts 现在包含一个 Int 值
someInts = [] // someInts 现在是空数组,但仍然是 [Int] 类型

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

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

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

var anotherThreedoubles = Array(repeating: 2.5, count: 3)
var sixDoubles = threeDoubles + anotherThreedoubles 
// sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

用数组字面量构造数组

var shoppingList: [String] = ["Eggs", "Milk"] 
// shoppingList 的构造函数也可以这么写:var shoppingList = ["Eggs", "Milk"]

访问和修改数组

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

获取数组中的数据项数量:

print(shoppingList.count)

检查 count 是否为 0:

if shoppingList.isEmpty {
    print("The shopping list is empty.")
} else {
    print("The shopping list is not empty.")
}

在数组后面添加新的数据项:

shoppingList.append("Flour")
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
print(shoppingList)

使用下标语法获取数组中的数据项:

print(shoppingList[0])

用下标来改变某个已有索引值对应的数据值:

shoppingList[0] = "Six eggs"

用下标来改变一系列数据值,新数据和原有数据的数量可以不一样:

shoppingList[4...6] = ["Bananas", "Apples"]

在某个具体索引值之前添加数据项:

shoppingList.insert("Maple Syrup", at: 0)

移除数组中的某一项,返回值为这个被移除的数据项:

shoppingList.remove(at: 0)

移除数组中的最后一项:

shoppingList.removeLast()

注意:不可以用下标访问的形式去在数组尾部添加新项。如果对索引越界的数据进行检索或设置新值,会引发一个运行时错误。

数组的遍历

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

for item in shoppingList {
    print(item)
}

如果需要每个数据项的值和索引值,可以使用 enumerated() 方法进行数组遍历,enumerated() 返回一个由每一个数据项索引值和数据值组成的元组,可以把这个元组分解成临时常量或变量进行遍历。

for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")
}

集合(Sets)

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

集合类型的哈希值

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

所有基本类型(如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 表示集合中允许存储的类型。和数组不同的是,集合没有等价的简化形式。

创建和构造一个空的集合

var letters = Set<Character>() // letters 变量的类型被推断为 Set<Character>
letters.insert("a")

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

letters = [] // letters 现在是一个空的 Set,但是它依然是 Set<Character> 类型

用数组字面量创建集合

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

favoriteGenres 的构造形式可以简化为:var favoriteGenres: Set = [“Rock”, “Classical”, “Hip hop”]。

访问和修改一个集合

找出一个 Set 中元素的数量:

print(favoriteGenres.count)

检查 Set 的 count 属性是否为 0:

print(favoriteGenres.isEmpty)

添加新元素:

favoriteGenres.insert("Jazz")

删除一个元素,返回被删除的元素值:

print(favoriteGenres.remove("Rock")!)

检查 Set 中是否包含特定值:

print(favoriteGenres.contains("Funk"))

遍历一个集合

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

for genre in favoriteGenres {
    print(genre)
}

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

for genre in favoriteGenres.sorted() {
    print(genre)
}

集合操作

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

基本集合操作

使用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(oddDigits.union(evenDigits).sorted()) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(oddDigits.intersection(evenDigits).sorted()) // []
print(oddDigits.subtracting(singleDigitPrimeNumbers).sorted()) // [1, 9]
print(oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()) // [1, 2, 9]

集合成员关系和相等

使用“是否相等”运算符(==)来判断两个集合是否包含全部相同的值。
使用 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

字典

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

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

字典类型简化语法

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

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

我们也可以用[Key: Value]这样简化的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选。

创建一个空字典

var nameOfIntegers = [Int: String]() 
// var nameOfIntegers = Dictionary<Int, String>() 也可以创建相同的空字典
nameOfIntegers[16] = "sixteen" 
// nameOfIntegers 现在包含一个键值对
nameOfIntegers = [:] 
// nameOfIntegers 又成为一个 [Int: String] 类型的空字典

用字典字面量创建字典

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

访问和修改字典

获取字典的数据项数量:

print(airports.count)

检查字典的 count 属性是否为 0:

if airports.isEmpty {
    print("The airports dictionary is empty.")
} else {
    print("The airports dictionary is not empty.")
}

使用下标语法添加新的数据项:

airports["LHR"] = "London"

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

airports["LHR"] = "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).")
}

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

if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName).")
} else {
    print("That airport is not in the airports dictionary.")
}

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

airports["APL"] = "Apple Internation"
airports["APL"] = nil

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

if let removedValue = airports.removeValue(forKey: "DUB") {
    print("The removed airport's name is \(removedValue).")
} else {
    print("The airports dictionary does not contain a value for DUB.")
}

字典遍历

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

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}

遍历字典的键或值:

for airportCode in airports.keys {
    print(airportCode)
}
for airportName in airports.values {
    print(airportName)
}

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

let airportCodes = [String](airports.keys) 
// airportCodes 是 ["YYZ", "LHR"]
let airportNames = [String](airports.values) 
// airportNames 是 ["Toronto Pearson", "London Heathrow"]

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值