集合类型

上一篇:控制流
当前篇:集合类型
下一篇:基础大杂烩

这一课我们将学习更加抽象的数据类型:集合类型 ,集合类型是用来存在某个特定类型实例的集合,就像鞋柜,它有很多格子,专门用来装鞋,我们打开几号格子的门就能取出其中的鞋子,当然生活中我们可以在鞋柜中放什么都行,代码中不行,集合中只能放指定类型的实例

Swift 为我们提供了 ArraySetDictionary 三种集合类型,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)

在黑框中我们没有看到输出任何的整数,而是输出了一个 nilnil 这个特殊符号在 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)

输出结果貌似跟预期的不一样,因为 keyvalue 是一一对应的相关联的,遍历的时候如果只给出 value ,那么我们就无法知道这个 valuekey 是什么,因此需要将它们捆绑在一起,这就叫 元组 ,它把多个值组合成一个复合值,并且任何类型的值都可以往里放

我们可以通过名称或者下标来访问 元组 中的某个值

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 为我们提供的基础类型基本上就快讲完了,基础的东西本来就很少,我们使用最多的就是整数类型,浮点数类型,字符串类型,布尔类型,集合类型,以及这些类型的可选类型,基础知识很枯燥无聊,但是也很重要,下一课我们将剩余的基础知识全部过一遍,无须全部记住,先形成一个概念,在进阶学习中一点一滴地巩固基础知识

上一篇:控制流
当前篇:集合类型
下一篇:基础大杂烩

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容