1.4 数组
我们在生活中常常需要使用集合的概念,比方说我们去市场买葡萄,我们不太可能只买一粒葡萄,对吧?我们应该买的是一串。这就是一个集合的概念。数组是一个用来表达集合概念的数据结构。数据结构本身和编程语言是无关的,编程语言只是对数据结构提供了一种实现,而一种数据结构所表现出来的性质,在任何语言中都应该是一致的。所以我们首先看一下数组这个数据结构的性质。
1、数组的性质
我们之前把一个变量比作为盒子,那么其实我们可以把数组看做是一系列连续的盒子。每个盒子在这个序列中都有一个唯一的编号,编号从零开始。
这个编号非同小可,数组的管理操作几乎都是围绕这个“编号”展开的。
- 在数组的末尾新增元素
- 在数组的任意位置新增元素
- 删除末尾元素
- 删除任意元素
在上面的示例中我们可以发现,数组的操作无论是新增还是修改,只要这个操作不是在数组的末尾进行的,那么就会产生数组元素的移动。因为数组不允许元素之间存在“缝隙”。当数组的元素较多的时候,这种移动会产生性能消耗。我们可以认为数组并不擅长处理非末尾元素位置的数据增加和删除操作。我们在设计数组的应用场景时,应该尽可能的避免这种操作。
注:由于swift是值类型,并且采用了“写时复制“的方式来提升性能,可以查看另外一篇文章来了解 什么是值类型
- 数组元素的任意访问
数组最擅长的事情是:根据索引标号,进行元素的随机访问。对于任意一个数组来说,这个数组内的所有”盒子”大小都是固定的,所以我们只要知道第一个盒子所在的位置,那么通过偏移量就可以得到任意一个盒子的位置。我们假设第一个盒子的地址是address1,那么第二个盒子的地址就是address1 加上偏移量 1,即:
第二个盒子的地址 = address1 + 1
第三个盒子的地址 = address1 + 2
第N个盒子的地址 = address1 + n - 1
第一个盒子的地址 = address1 + 0
我们在这里把地址抽象为字符串address,把偏移量的大小抽象成了单位1
2、swift语言对数组的创建于管理
- 数组的定义
数组在swift中使用泛型的struct进行定义的,所以我们可以这样来创建一个整形数组
var array = Array<Int>()
如果我们希望在定义的时候,给数组一些初始值,那么我们可以可以这样操作。
var array = Array<Int>(arrayLiteral: 1,2,3,4)
如果你觉得这样有点麻烦,没关系,我们还可以这样来创建。
var array = ["a","b","c","d"]
这时候我们要强调的是,数组array是什么类型的呢?我们知道swift是静态强类型的语言,它不可能允许array在被赋值以后没有类型,这个时候一定是类型推断机制已经给数据定义了一个类型。那么这个推断的根据是什么呢?
数组的类型,是由数组元素的类型所决定的,当前数组的元素包括“a”,那么类型推断机制会将array判定为“String类型数组“。需要强调的是,数组元素不可以是混合类型,既所有元素的类型必须一致。
像之前声明的变量方式一样,我们也可以通过显示声明的方式,直接为数组指定一个类型。
var array:[String] = ["a","b","c","d"]
在显示声明的时候,类型需要用[]括起来,在视觉上,是不是和值["a","b","c","d"]非常的统一!
- 数组元素的随机访问
在前面我们说到了,如果我们有数组的起始地址(第一个盒子的地址),那么我们就可以通过地址偏移量来计算任意个盒子的位置。其实当我们创建了一个数组以后,计算机其实在用数组的名字指向了这个数组的起始地址。所以我么可以这样来访问数组。
var array:[String] = ["a","b","c","d"]
array[0] ----> "a"
array[1] ----> "b"
array[2] ----> "c"
array[3] ----> "d"
- swift数组元素的增删统计
swift数组元素的增加,删除操作需要使用数组对象提供的方法,如果你现在还没能了解什么是属性和方法
也没有关系,因为这些内容在文档当中都有详细的描述,我们可以在需要的时候随时查阅。我们目前能够了解数组数据结构的基本特性就好了
var array:[String] = ["a","b","c","d"]
array.count // 4,统计一个数组中的元素个数
array.isEmpty // false 判断一个数组是不是空,如果为空则返回false
在末尾追加元素
array.append("e") // 在数组的末尾新增元素
另外一种追加元素的写法,与上面的方法等效
array += [5]
并且可以一次追加多个值
array += [5,6,7,8]
array.insert("f",atIndex: 1) // 在索引1的位置插入新的元素“f”,注意会引起数组元素的移动
array.removeLast() // 删除数组中的末尾元素
array.removeAtIndex(0) // 删除数组索引位置为0 的元素,既元素“a”,注意会一起数组元素的移动
array.removeAll() // 清空数组的所有元素
数组在swift中被设计为值类型,值类型的性质主要体现在赋值操作和传递过程中,但数组本质上还是一个引用类型,就像我们上面提到的,我们可以对一个数组进行各种修改,只要我们不是容量引起数组重新分配内存,则数组的地址不会产生变化。
func address(o: UnsafePointer<Void>) -> String {
return String.init(format: "%018p", unsafeBitCast(o, Int.self))
}
我们定义一个数组,并固定数组的大小,防止因为数组扩容而导致新建数组。
var x: [Int] = []
x.reserveCapacity(1000) // 固定数组长度
print(address(&x)) // 打印数组地址 "0x00007f880a038020\n"
x += [1,2,3,4,5]
print(address(&x)) // 改变数组后打印数组地址 "0x00007f880a038020\n"
x.insert(2, atIndex: 2)
print(address(&x)) // 在次改变后打印数组 "0x00007f880a038020\n"
x.removeAll(keepCapacity: true)
print(address(&x)) // 清空后,再次打印数组 "0x00007f880a038020\n"