[极速Swift教程之二] 复合类型

633 阅读10分钟
更多内容,欢迎关注公众号:Swift花园
喜欢文章,不如来点赞关注吧

 

数组

数组是用于存储一组值的合集。举个例子,红、绿、蓝是三基色,你可以对它们进行调和从而产生新颜色。因此颜色就可以用一个包含了红、绿、蓝三个值的数组来存储。

代码上,我们这么书写:

let red = 1.0
let green = 1.0
let blue = 1.0
let color = [red, green, blue]

上面的最后一行代码创建了一个数组:它以方括号开始和结束,里面的元素通过逗号分隔。

你可以通过书写数组名加上内部包含一个数字的方括号来读取数组里的元素。注意:这个数字代表元素在数组里的位置,几乎在所有的计算机编程语言中,这个位置都是从0开始的。所以,如果你想读取蓝色,可以这么写:

color[2]

小心:如果你尝试读取一个不存在的值,Swift将会崩溃。例如,试图读取color[5]就不是一个好主意。

另外还要注意的是,如果你想使用类型注解,数组是通过方括号加里面的类型来表示的: [String],[Int],[Double] 和 [Bool]。

 

集合

集合跟数组类似,也是存储一组值的合集。但它们有两个区别:集合中的元素是随机存放的,而数组中的元素是顺序存放的。集合中不允许有两个相同的元素,即任何一个元素只能出现一次。数组则没有这个限制。

你可以直接利用数组来创建一个集合,就像这样:

let colors = Set(["red", "green", "blue"])

当你在Playground中查看colors中的元素时,你看到的元素的顺序可能跟你创建colors时填写的顺序不同。实际上,这个顺序并非真的是每次都随机生成,只不过很有可能跟你给的顺序不同。

Swift内部自有算法来生成这个顺序,但不保证跟你创建集合时传入的顺序一致。所以,通过位置来读取数组中的元素那种方式,对于读取集合中的元素是不适用的。

如果你试图往集合中插入一个已经存在的元素,重复的这个元素会被忽略。举个例子:

let colors2 = Set(["red", "green", "blue", "red", "blue"])

最后的colors2只会包含red,green和blue各一个。

 

元组

元组允许你通过一个值来存储几个值。 听起来跟数组很像,但元组有所不同:你不能往元组中添加或者删除元素 —— 元组的长度是固定的。你不能改变元组中元素的类型 —— 元组创建时内部的元素类型必须是一致的。

你可以通过位置或者名称来访问元组中的元素。但是Swift不允许你访问不存在的位置或者名称。

元组的创建方式是把一组值放进圆括号中,就像这样:

var name = (first: "Taylor", last: "Swift")

你可以通过从0之类的位置信息来访问元组的元素:

name.0

或者你也可以通过名称来访问元组的元素:

name.first

在元组创建之后,你可以改变其中元素的值,但是不能改变它们的类型。因此,如果你尝试把name改成(first: "韩梅梅",age: 25),将会遭遇错误。

 

数组 vs 集合 vs 元组

数组,集合和元组第一眼看上去很相似,但它们的用途是有区别的。为了帮助你更好地选择,这里有一些规则供参考:

  • 如果你需要存放的元素是关联在一起被使用的,它们的个数固定,顺序或者名称也相对固定,那么你应当使用元组。举个例子:
let address = (门牌号: 666, 街道: "朝阳区某街道", 城市: "北京")
  • 如果你需要一个合集,其中的元素必须是唯一的,或者你可能需要快速地检验合集里有没有包含某个元素,那么你应当使用集合。
let shoppingList = Set(["苹果", "肉", "牛奶"])
  • 如果你需要一个合集,其中的元素允许重复,或者合集中元素的顺序对你来说很重要,那么你应当使用数组。
let pingpongScores = ["11-7", "8-11", "11-9", "8-11", "11-5", "13-11"]

三种类型中,数组是最常用的。

 

字典

字典也是用于存储一组值的合集。但跟数组用整数的位置来访问元素的方式不同的是,在字典里你可以用任何你想要的类型来充当访问标识。

当然,最常见的存储字典的访问标识是字符串。举个例子,我们可以创建一个基于运动员的名字来访问运动员的身高的字典:

let top3NBAHeights = [
  "马努特 波尔": 2.31,
  "姚明": 2.28,
  "里克 施密茨": 2.23
]

就像数组一样,字典也以方括号开始和结束,里面的元素通过逗号分隔。此外,为了把一个值(例如2.28)存放到对应的标识(例如“姚明”)下面,你还得用到一个冒号。

这些访问标识被称为“键”,相对应的是“值”。我们用键来读取值。

top3NBAHeights["姚明"]

注意: 如果你想使用类型注解,数组是通过方括号加里面的键值对类型来表示的,键值对又通过冒号分隔。举个例子,[String: Double] 或者 [String: String]。

 

字典默认值

如果你试图通过一个不存在于字典中的键读取字典中的值时,Swift会返回给你一个nil。它代表没有东西。当然,这可能就是你想要Swift在找不到目标时返回给你的结果。不过,我们还有另外一种选择:当给定的键对应的值不存在时,我们可以指定一个默认的值返回给访问者。

为了演示这种操作,我们先创建一个包含了两个人最喜欢的冰淇淋的字典:

let favoriteIceCream = [
  "小明": "巧克力",
  "小红": "香草"
]

我们可以像这样读取小明最喜欢的冰淇淋:

favoriteIceCream["小明"]

但是当我们试图读取小华最喜欢的冰淇淋时,我们会得到一个nil,这代表Swift没有在字典里找到对应“小华”这个键的值。

favoriteIceCream["小华"]

这种情况下,我们可以通过指定一个默认值,比如说“未知”,来解决这个问题。这样做之后, 当Swift没有从字典中找到小华最喜欢的冰淇淋时,我们得到的不再是一个nil,而是一个“未知”字符串。

favoriteIceCream["小华", default: "未知"]

 

创建空的合集

数组,集合和字典都属于合集,因为它们都是把分散的值收集起来放在一处的结构。

如果你想要创建一个空的合集,只需要写上类型名,然后加上一组圆括号。举个例子,创建一个键和值的类型都是String的字典,我们可以这样书写:

var teams = [String: String]()

之后添加元素,就像这样:

teams["小红"] = "红色"

类似的,你可以像这样创建一个Int类型的空数组:

var results = [Int]()

创建集合是一个例外,方式稍有不同:

var words = Set<String>()
var numbers = Set<Int>()

这是因为Swift只为字典和数组提供了特殊的语法,其他类型都必须使用尖括号来声明类型信息,就像集合那样。

当然,也可以不使用特殊语法,通过和集合类似的标准方式创建字典和数组:

var scores = Dictionary<String, Int>()
var results = Array<Int>()

  

枚举

枚举,通常简称enums,是一种定义一组高度关联的值的方式。它使得这组关联的值使用起来更方便。为什么这么说呢?

想象一下,如果你想要写一些代码表示正在做的某件事情的结果,成功或者失败,你选择了使用字符串来表示这个结果:

let result = "failure"

不过,有人不小心写错了或者采用了不同的拼写,就像下面这样:

let result2 = "failed"
let result3 = "fail"

上面几行表示失败的字符串都各不相同,所以它们代表不一样的东西,无法让你基于失败这一结果的代码被正确执行。

而有了枚举,我们可以定义一个叫做Result的类型,它既可以是success,也可以是failure,就像这样:

enum Result {
  case success
  case failure
}

现在,当我们再想要表示结果时,只需要从两个值中选择一个:

let result4 = Result.failure

这样做的好处是可以避免书写不一致字符串导致跟预期不匹配的情况发生。

  

枚举关联值

除了可以存储简单的值,枚举还可以存储附属于每个case的关联值。这个特性使得你可以为枚举附加额外的数据,从而让它们传达更多细微的信息。

举个例子,我们可以定义一个枚举,它存放了各种各样的活动类型:

enum Activity {
  case bored
  case running
  case talking
  case singing
}

上面的枚举类型存储的信息,可以让我们知道有人在讲话,但我们不知道讲话的内容,或者可以让我们知道有人在跑步,但我们不知道他们将跑去哪里。

通过枚举的关联值,我们添加额外的细节:

enum Activity {
  case bored
  case running(destination: String)
  case talking(topic: String)
  case singing(volume: Int)
}

现在我们有了更精确的信息。我们可以说某人正在谈论足球:

let talking = Activity.talking(topic: "football")

枚举原始值

有的时候你可能想给枚举赋予一些原始值从而让它们可以表达某种含义。这么做可以让你动态地创建枚举,并且以不一样的方式来使用它们。

举个例子,你可以创建一个名叫Planet的枚举,然后让它的每条case存储一个整数:

enum Planet: Int {
  case mercury
  case venus
  case earth
  case mars
}

Swift会为这些case自动分配一个数字,同样的,是从0开始。你可以利用这些数字创建枚举的case。举个例子,earth会被分配到数字2,于是你可以这么创建一个earth的case:

let earth = Planet(rawValue: 2)

如果你自行指定了一个或者几个case的原始值,Swift可能会自动为剩下的case生成原始值,只要这些值是可以被Swift推断的。比如把地球算作第2颗行星来考虑可能不太自然(因为我们一般说它是九大行星中的第3颗),于是你可以这么写:

enum Planet: Int {
  case mercury = 1
  case venus
  case earth
  case mars
}

这样一来Swift就会给mercury分配数字1,后面的数字基于前面的往上加,earth就变成第3颗行星了。

 

总结

让我们来总结一下。

  • 数组,集合,元组和字典让你可以通过单一对象存储一组值。它们实现这一点的方式各有不同,所以你需要根据具体场景来决定采用哪一种结构。
  • 数组按顺序存储数据,你可以往里面添加或者删除元素,并通过位置来读取。
  • 集合不按顺序存储数据,所以你不能通过位置来访问,但它提供了元素不重复的特性。
  • 元组长度固定,你可以给里面的每个元素取名。这样就既可以通过位置,也可以通过名字来读取。
  • 字典根据键来存储数据,因此读取元素时要用到键。
  • 枚举是一种分组关联值的方法,帮你避免拼写错误。
  • 你可以为枚举的每条case设置原始值,同时还可以为它们附加关联值来提供额外的信息。

 

我的公众号

这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~