Swift的方式来使用数组
小时光
Array是在开发中最常用的数据类型之一,官方文档对Array的定义是:An ordered, random-access collection.
。通常表示一个有序的集合,这里所说的有序并不是大小有序,而是指Array
中元素的先后位置关系。
1、Swift的方式来操作数组
1.1、创建Array的N种方法
1 | var testArray1: Array<Int> = Array<Int>() |
在上面的代码块中,Array<Int>
和[Int]()
没有本质区别,随便使用哪种方式都可以初始化一个Array
。而最后一种直接使用了一个空的Array
生成了一个新的Array
对象。
1.2、定义数组时同时指定初始值的方法
1 | var testInts = [Int](repeating: 6, count: 3) // [6, 6, 6] |
1.3、count和isEmtpy属性
count
返回数组集合中元素的个数,类型是Int
1 | let testArray = ["one","two","three","four","five","six"] |
isEmpty
表示数组是否为空,类型是Bool
再对数组操作前我们可以先判断数组是否为空再下一步操作
1 | let testArray = ["one","two","three","four","five","six"] |
1.4、访问Array中的元素
1.4.1、通过索引访问数组的某个元素
在几乎所有的开发语言中都惯用索引(或下标)的方式访问数组数组中的元素,因为我们在使用索引访问数组的元素时没法保证索引的安全性,所以这种方式存在数组越界的风险,稍不注意就会导致整个程序直接Crash
,从而给用户造成不好的体验。
当然Swift
也支持这种方式,但苹果却不推荐开发者使用这种方式访问数组元素。
1 | let testArray = ["one","two","three","four","five","six"] |
如上面的代码,使用这种方式访问数组元素,如果直接访问不做处理会导致程序直接crash
,在调试时,可以看到编译器直接报错:Thread 1: Fatal error: Index out of range
, 所以在使用索引访问数组元素时应该慎重在慎重。那么在Swift
中如何优雅的访问数组元素呢?事实上,Swift的设计者是不推荐开发者使用这种下标取值的方式。
使用type方法可以看到,这种方式返回的值为String
类型,这说明在使用下标取值时需要确认,取到的值为String类型,而不是Optionals<String>
类型,我们在使用这种方式取值时没有Optionals
保护数组越界的情况,在使用下标取值的方法时我们就要承担crash的风险。
1.4.2、访问数组中某个范围内的元素
在Swift
中我们可以通过使用range operator
访问数组的一个范围,通过range operator
方式得到的并不是一个Array
,而是一个ArraySlice,官方文档对ArraySlice
的定义是A slice of an
Array,
ContiguousArray, or
ArraySlice instance.
通俗来说,就是Array
某一段内容的View
,不保存数组的内容,只保存这个view
引用的数组的范围。我们可以通过这个view
创建一个新的Array
。
1 | let testArray = ["one","two","three","four","five","six"] |
1.4.3、向数组中添加或删除元素
向数组的末尾添加元素,可以使用append
方法, 或者使用+=
的方式:
1 | var testArray = ["one","two","three","four","five","six"] |
如果要在Array
中间位置添加元素,可使用insert
方法:
1 | var testArray = ["one","three","four","five","six"] |
insert
方法的第一个参数表示要插入的值,第二个参数表示要插入的位置索引,此处要注意的是:这个位置必须是一个合法的范围,即0...array1.endIndex
,如果超出这个范围就会导致程序Crash
删除数组中的元素:
删除数组中的最后一个元素可以使用removeLast()
方法,删除数组中的第一个元素可以使用removeFirst()
方法,
1 | var testArray = ["one","two","three","four","five","six","seven"] |
当我们删除数组中的某个元素时,首先要确保数组不能为空,然后还要保证删除的索引在合法的范围内,然后在进行删除操作
查找元素在数组中的位置:
如我要查找元素5在数组中的位置,可以使用firstIndex
,返回的是一个Optional<Int>
1 | let testList = [1,2,3,4,5,6] |
#####1.5、数组遍历
在开发中对Array
每个元素的遍历也是常用操作:
1.5.1、最简单的方式
1 | let testArray = ["one","two","three","four","five","six"] |
1.5.2、遍历的时候,同时获得索引和值
使用数组对象的enumerated()
方法,它会返回一个Sequence
对象,包含了每个成员的索引和值。
1 | let testArray = ["one","two","three","four","five","six"] |
1.5.3、通过闭包closure
遍历
通过闭包closure
,我们可以使用Array
的forEach
方法来遍历数组:
1 | let testArray = ["one","two","three","four","five","six"] |
for in
和 forEach
遍历的效果是一样的,但是如果使用forEach
方法遍历数组就不能通过break
或continue
来退出循环,如果要退出遍历就只能使用for in
方法。
1 | testArray.forEach { (testChar) in |
1.5.4、map 、flatMap、 compactMap 、 filter 遍历数组
事实上,Swift的设计者并不推荐开发者使用传统的C语言风格的for循环。Swift为开发者提供了更高级的方法,当我们需要循环一个数组时,可以根据自己的需求,使用Map 和 Filter来实现。
举例一:老师现在已经得到了学生的试卷成绩,再为每个学生加上平时成绩30分,这样可以得出学生的最终成绩,如果使用传统的C语言循环来实现:
1 | let fractionArray: [Int] = [40,53,59,43,56,54,33,55,66,70,22,69] |
再看看Swift为开发者提供的map方法:
1 | let fractionArray: [Int] = [40,53,59,43,56,54,33,55,66,70,22,69] |
map
用于把数组中的所有元素按照指定规则操作变换并返回一个新的数组,这样比使用for循环更具表现力。
使用map
来完成这类操作,大大提高了代码的可读性;
同时使用map
直接返回了一个常量数组;
当然Swift
的设计者只是对map
封装了for
循环的代码,其核心代码如下:
1 | extension Array { |
flatMap和compactMap是map的升级版:
flatMap遍历时会把二维数组变为一维数组。
compactMap在遍历数组的同时,去除其中的nil值。
1 | let lsArray: [Int] = testList.compactMap { (num) -> Int? in |
举例二: 老师在算出学生成绩后要查看那些人的成绩是优秀,此时我们可以使用filter
1 | let excellentArray = finallyArray.filter { |
map在遍历数组的同时可以对每个参数做指定规则的操作,同时返回一个新的数组;
filter只按指定规则遍历数组,同时返回一个新的数组
filter
是怎么实现的呢,我们可以根据map
的实现方法来实现,其核心代码如下:
1 | extension Array { |
1.5.5、min 、 max
同样老师如果需要计算本次考试的最高分和最低分,只要数组中的元素实现了Equatable protocol
协议,开发者不用对数组进行任何操作,可直接调用min 、 max
即可:
1 | finallyArray.min() // 52 |
1.5.6、sorted
和partition
对数组进行排序
接上面,如果老师要对本班成绩按从大到小或者从小到大的顺序排列,这样可以更直观的看到本班的成绩:
1 | let mixArray = finallyArray.sorted() // [52, 63, 70, 73, 83, 84, 85, 86, 89, 96, 99, 100] |
1.5.7、判断两个数组是否相等
1 | finallyArray.elementsEqual(mixArray, by: { |
1.5.8、数组是否是以特定的排序开头
如老师要计算出本次考试成绩,是否有满分的:
1 | mixArray.starts(with: [100], by: { |
1.5.9、计算数组中元素和
例如老师要计算本次考试的总成绩,然后计算出本次考试的平均成绩:
如果使用C语言风格,我们首先要对成绩做一个遍历,然后累加最终得到全班成绩总和:
1 | var allNum = Int() |
Swift
为开发者提供了更简单有效的方法:
1 | finallyList.reduce(0, +) / (finallyList.count) // 81 |
1.5.10、按条件把数组中的元素分类
例如老师要分别统计出及格和没及格的成绩,我们认为60分为及格:
1 | let pass = mixArray.partition(by: { |
2、解决数组越界问题
通过对数组扩展全局处理数组越界问题:
1 | extension Collection { |
3、Array和NSArray
Array
是结构体,属于值类型,NSArray
是类,属于引用类型。Array
是否可以被修改完全是通过var
和let
关键字来决定的,Array
类型自身并不解决它是否可以被修改。Array
如何转换为NSArray
参考: stackoverflow Swift - How to convert a swift array to NSArray?
赋值时内存
看下面两段代码
(1) 、
Array
的copy
1
2
3
4var testArray = ["one","two","three","four","five","six"]
let copyArray = testArray
testArray.append("seven") // ["one", "two", "three", "four", "five", "six", "seven"]
print(copyArray) // ["one", "two", "three", "four", "five", "six"](2) 、
NSArray
的copy
1
2
3
4let mutArray = NSMutableArray(array: ["one","two","three","four","five","six"])
let copyArray: NSArray = mutArray
mutArray.insert("seven", at: 6) // ["one", "two", "three", "four", "five", "six", "seven"]
print(copyArray) // ["one", "two", "three", "four", "five", "six", "seven"]第一段代码中:复制
testArray
数组后并向testArray
中添加元素,copyArray
的内容并不会改变,因为在复制testArray
数组时,并不是内容复制,而是内存地址复制,两个数组仍旧共用同一个内存地址,只有当修改了其中一个Array
的内容时,才会生成一个新的内存。第二段代码:复制
mutArray
数组,尽管copyArray
为NSArray
,此时我希望copyArray
的值是不会改变的,但实际上当我修改了mutArray
的值后copyArray
的值也随之改变了。由于这个赋值执行的是引用拷贝,这两个数组指向的是同一个内存地址,所以当我们修改mutArray
的内容时,copyArray
也就间接受到了影响。- 当我门使用
NSArray
和NSMutableArray
时,Swift
种的var
和let
关键字就不起作用了。
本文主要介绍了
Swift
中最常用的集合Array
的一些基本用法,和Objective-C
的NSArray
的区别。友情链接:
- 当我门使用