这一课我们将学习更加抽象的数据类型:集合类型 ,集合类型是用来存在某个特定类型实例的集合,就像鞋柜,它有很多格子,专门用来装鞋,我们打开几号格子的门就能取出其中的鞋子,当然生活中我们可以在鞋柜中放什么都行,代码中不行,集合中只能放指定类型的实例
Swift 为我们提供了 Array、Set、Dictionary 三种集合类型,Array 被称为数组,它按先后顺序存放一组数据;Set 被称为集合 ,它没有顺序,但是也没有重复数据,也就是说一个集合中只可能有一个 1 ;Dictionary 被称为字典,它是一个无序的键值对的数据集,也就是一个键对应一个值,就像我们用英文字典查某个单词,一定是在首字母对应的区域去找,这里的首字母就是键 key ,这个单词就是值 value
集合类型有 可变 和 不可变 之分,还记得前面学过的常量 let 和变量 var 吗,常量集合类型就是不可变集合,一旦赋值就不可更改,变量集合类型就是可变集合 ,只有可变集合才允许往其中添加或删除数据
数组(Array)
我们先来创建一个空的用于存放整数的数组:
var numbers = Array<Int>.init()
Array 表示数组类型,<>表示指定数组中存放数据的类型,我们指定的是 Int 类型,init() 是初始化(initialize)的意思,这句话的意思是:创建一个存放整数类型的数组,并赋给变量 numbers
我首先把最难看也是最完整的创建数组的方式展示出来,一是先苦后甜,二是展示Swift的语法糖,后面我们将一步一步的缩略这一行创建数组的代码
任何类型都有初始化方法,使用初始化方法才能创建出一个实例,初始化方法 init() 可以省略成 () :
var numbers = Array<Int>()
Array<Int> 类型可以用简化方式 [Int] 表示,所以代码最简方式如下:
var numbers = [Int]()
记住这种创建数组的方式,因为这是我们用的最多的方式,一切从简,为了全面理解实例的创建方式,下面我贴出一些代码供参考:
var numbers: Array<Int> = []
var numbers: [Int] = []
var letters = [String]() //这是一个字符串数组
使用字面量创建数组:
let a = Int.init(1)
上面这句话跟 let a = 1 是一个意思,只不过我们平常不这样写,太麻烦,像 let a = 1 这样的创建方式叫做字面量,可以理解为键盘能敲出来的东西,数组同样可以用字面量来创建:
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let fruits = ["Apple", "Banana", "Pear", "🍊", "🍇"]
还记得自动类型推断吗,numbers 自动推断为 [Int] ,fruits 自动推断为 [String],使用字面量创建数组时不要使用不同的类型,虽然数组中可以存放不同类型的数据,但是本节课不学习这个知识点
相同类型的数组可以使用 + 运算来合成新的数组:
let yourSnacks = ["Cookies", "Potato chips", "Sugar", "Chocolate", "LaTiao", "ShaQiMa"]
let mySnacks = ["FangBianMian"]
let ourSnacks = yourSnacks + mySnacks
添加修改数据
我们可以向可变数组中添加和插入数据
var numbers = [1, 2, 3, 4, 5, 6, 7, 8]
numbers.append(9) //在数组最后添加整数 9
print(numbers)
numbers.insert(0, at: 0) //在数组的第 0 个位置插入整数 0
print(numbers)
let number = numbers[2] //访问第3个位置上的元素
print(number) // 2
numbers[2] = -1 //将第三个位置上的元素替换为 -1
append 和 insert 是数组本身的功能函数,调用函数使用 . 加函数名加函数参数,函数的知识后续课程会详细学习,访问数组中某个位置的元素使用 [位置下标]
数组越界:insert 以及通过下标访问数组会存在数组越界的风险,也就是说,如果数组总共只有10个数据,我们要是在第100的位置上插入数据或者访问第100位置上的元素,都会造成程序崩溃,所以当我们在不确定是否会越界的时候,为了程序的稳定性,需要做一些判断:
var numbers = [1, 2, 3, 4, 5, 6, 7, 8]
numbers.append(9)
print(numbers)
let insertIndex = 0
if insertIndex >= 0 && insertIndex <= numbers.count {
numbers.insert(0, at: insertIndex)
}
print(numbers)
let visitIndex = 2
if visitIndex >= 0 && visitIndex < numbers.count {
let number = numbers[2]
print(number)
numbers[2] = -1
}
删除数据
可变数组中的数据也可以删除
var yourSnacks = ["Cookies", "Potato chips", "Sugar", "Chocolate", "LaTiao", "ShaQiMa"]
yourSnacks.removeFirst() //删除第一个数据
yourSnacks.removeLast() //删除最后一个数据
yourSnacks.remove(at: 1) //删除下标为1,也就是第2个数据
yourSnacks.removeAll() //删除所有数据
var yourSnacks = ["Cookies", "Potato chips", "Sugar", "Chocolate", "LaTiao", "ShaQiMa"]
yourSnacks.removeFirst(2) //删除前两个数据
yourSnacks.removeLast(2) //删除最后两个数据
//removeFirst 和 removeLast 有不同的用法,后面我们会学到,方法的多样化
遍历数组
var yourSnacks = ["Cookies", "Potato chips", "Sugar", "Chocolate", "LaTiao", "ShaQiMa"]
for snack in yourSnacks {
print(snack)
}
for index in 0..<yourSnacks.count {
print(yourSnacks[index])
}
var index = 0
while index < yourSnacks.count {
print(yourSnacks[index])
index += 1
}
实际上数组还有很多很多的用法,我们先了解这些最基本的增删改查就够了,实际开发中需要的时候,我们再去了解更多的用法。Swift为我们提供的任何一个类型都会有很多很多的属性和方法,在学习的时候不需要一次性了解全部,可以一边开发一边了解
集合(Set)
Set 跟数组类似,但是特点不一样,Set 是无序的集合,也就是说不能通过下标位置来访问数组,Set 中没有重复数据,也就是说不管你在一个整数集合中插入多少个 1 ,其中只会有一个 1
下面我们来创建一个字符串集合,简写形式以及被数组占用了,所以集合就得老老实实写类型定义了,毕竟数组比集合更常用
var fruits = Set<String>()
fruits.insert("Apple")
fruits.insert("Banana")
fruits.insert("Pear")
fruits.insert("Apple") //集合里仍然只有一个唯一的苹果
fruits.remove("Apple") //集合里有苹果,则把苹果删除
fruits.remove("Orange") //集合里并没有橘子,所以啥也没干
fruits.removeAll() //清空集合中的数据
我们可以通过字面量创建一个集合,他的形式跟数组字面量是一样的,只不过我们需要声明一下集合类型,因为自动类型推断以及给了数组
let fruits: Set<String> = ["Apple", "Banana", "Pear", "Orange", "Grape", "Apple"]
print(fruits)
//虽然我们在字面量里写了两个 Apple ,但是实际上创建完成后就只剩一个了
我们可以通过集合的长度,也就是其中的数据的数量,或者集合的 isEmpty 属性来判断集合是否是空的,我们可以用 contains 方法来判断集合中是否包含某个值
let fruits: Set<String> = ["Apple", "Banana", "Pear", "Orange", "Grape", "Apple"]
if fruits.count == 0 {
print("fruits set is empty")
}
if fruits.isEmpty {
print("fruits set is empty")
}
if fruits.contains("Apple") {
print("fruits set contains Apple")
}
集合也是可以遍历的,但是集合是无序的,所以我们只能用 for-in 语句来遍历,并且其中元素被访问的先后顺序也是不确定的
let fruits: Set<String> = ["Apple", "Banana", "Pear", "Orange", "Grape", "Apple"]
for fruit in fruits {
print(fruit)
}
集合还有一些方法,比如某个集合是否是另外一个集合的子集,是否包含在另一个集合中等等,跟数学里的集合是一个意思,实际上这些方法我至今也没用过,集合的作用主要用来避免重复数据以及判断包含关系
字典(Dictionary)
字典是一个键值对,一个键 key 对应一个值 value,字典中的元素同样没有顺序,我们需要通过 key 去访问对应的值。给字典指定数据类型的时候,我们需要分别指定 key 的类型和 value 的类型了,下面我们创建一个空的字典
var namesOfNumbers = Dictionary<Int, String>()
namesOfNumbers[0] = "zero" //现在 0 对应的值就是 "zero"了
字典也有对应的简写形式,非常直观
var namesOfNumbers = [Int : String]()
namesOfNumbers[0] = "zero" //现在 0 对应的值就是 "zero"了
namesOfNumbers[1] = "ome" //现在 1 对应的值就是 "ome"了
namesOfNumbers[1] = "one" //上面我们把"one"写成"ome"了,没关系,我们可以改回来
print(namesOfNumbers[1])
字典中的方括号不是指下标的意思,而是指 key ,下面我们用字符串来做 key
var numbersOfNames = [String : Int]()
numbersOfNames["zero"] = 0 //现在 "zero" 对应的值就是 0 了
numbersOfNames["one"] = 1 //现在 "one" 对应的值就是 1 了
numbersOfNames["two"] = 2 //现在 "two" 对应的值就是 2 了
print(numbersOfNames["two"])
我们也可以通过字面量来创建一个字典
var numbersOfNames = ["zero" : 0, "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5, "six" : 6, "nine" : 9]
冒号表示一个 key 对应一个 value ,冒号前面的是 key ,后面的是 value ,键值对之间用逗号隔开。这样一长串的字面量看起来很累,我们可以适当地运用换行来改善代码可读性
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
如果我们要删除某个 key 及其对应的值呢,可以用 removeValue(forKey:) 方法,清空字典用removeAll
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
numbersOfNames.removeValue(forKey: "five")
print(numbersOfNames)
numbersOfNames.removeAll()
if numbersOfNames.isEmpty {
print("no numbers")
}
下面我通过字典来引出下一课的知识点
可选类型
设想一下,当我们通过某个 key 去获取对应的值的时候,可能会出现两种情况,一种是取到值了,另一种是字典里没有我们想要的东西,这个时候怎么办呢,请看下面代码
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
let eleven = numbersOfNames["eleven"]
print(eleven)
在黑框中我们没有看到输出任何的整数,而是输出了一个 nil ,nil 这个特殊符号在 Swift 中非常重要,它代表 空 ,代表 不存在 ,现在我们把代码稍微改一下
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
let eleven = numbersOfNames["eleven"]
if eleven == nil {
print("There is no eleven")
} else {
print(eleven)
}
更重要的是,我们可能会认为 eleven 会被自动推断为 Int 类型,那么我们再把代码修改一下
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
let eleven: Int = numbersOfNames["eleven"]
if eleven == nil {
print("There is no eleven")
} else {
print(eleven)
}
编译器报错:“Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?”,也就是说 numberOfNames["eleven"] 获取到的值的类型是 Int? ,而不是 Int ,他们虽然基本上是一个类型,但又不完全一样,唯一的区别就在于,Int? 类型表示 Int 的可选类型,可选类型就代表它的值可能是 nil ,也可能是一个整数,任何类型都可以在后面加一个?表示成可选类型,实际上,任何字典通过 key 获取的值永远都是对应类型的可选类型
可选类型不难理解,比如说我的 地点,按道理说,无论我在什么地方,都会有一个确定点,因此我的 地点 就不需要声明为可选类型,但是我的 女朋友 就可能是 nil 了,那就需要将这个类型声明为可选类型 女朋友? ,这个问号很形象,好像是在提醒你 “有吗?”
元组
第二个要引出的新知识点是 元组,我们在讲字典的时候没有将字典的遍历,因为字典的遍历引出了 元组 这个知识点,请输入下面的代码查看运行结果
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
for number in numbersOfNames {
print(number)
}
//(key: "three", value: 3)
//(key: "one", value: 1)
//(key: "nine", value: 9)
//(key: "four", value: 4)
//(key: "five", value: 5)
//(key: "zero", value: 0)
//(key: "six", value: 6)
//(key: "two", value: 2)
输出结果貌似跟预期的不一样,因为 key 和 value 是一一对应的相关联的,遍历的时候如果只给出 value ,那么我们就无法知道这个 value 的 key 是什么,因此需要将它们捆绑在一起,这就叫 元组 ,它把多个值组合成一个复合值,并且任何类型的值都可以往里放
我们可以通过名称或者下标来访问 元组 中的某个值
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
for number in numbersOfNames {
print("number of \(number.key) is \(number.value)")
}
//number of three is 3
//number of one is 1
//number of nine is 9
//number of four is 4
//number of five is 5
//number of zero is 0
//number of six is 6
//number of two is 2
元组也可以通过顺序来访问其中的值
var numbersOfNames = ["zero" : 0,
"one" : 1,
"two" : 2,
"three" : 3,
"four" : 4,
"five" : 5,
"six" : 6,
"nine" : 9]
for number in numbersOfNames {
print("number of \(number.0) is \(number.1)")
}
//number of three is 3
//number of one is 1
//number of nine is 9
//number of four is 4
//number of five is 5
//number of zero is 0
//number of six is 6
//number of two is 2
现在,Swift 为我们提供的基础类型基本上就快讲完了,基础的东西本来就很少,我们使用最多的就是整数类型,浮点数类型,字符串类型,布尔类型,集合类型,以及这些类型的可选类型,基础知识很枯燥无聊,但是也很重要,下一课我们将剩余的基础知识全部过一遍,无须全部记住,先形成一个概念,在进阶学习中一点一滴地巩固基础知识