# Array

`Array` 是有序的，支持随机读写的集合类型

```swift
@frozen struct Array<Element>
```

![](http://pic.existorlive.cn/202206062006677.png)

## 1. 基本操作

### 1.1 初始化，构造

```swift

// 创建空的Array, 类型推断

let array1 = [Int]()

var array2 = Array<Int>()


// 创建空的Array, 指定类型

var array3 : [Int] = []

// 通过列表初始化

var array4 = [1,2,3,4,5]

// 调用init方法初始化

var array5 = Array(repeating:0, count:3)

```

### 1.2 增加元素

`+`，`append(_:)` 可以在数组尾部添加元素

`insert(_:,at:)` 在索引指定的位置插入元素

```swift

array1.append(11)
array1 += [13,12]

array1.insert(13,at:0)

```

### 1.3 删除元素

`remove(at:)` 删除索引指定位置的元素

`removeLast()` 删除数组中的最后一项

```swift
// 
array1.remove(at:5)

// Can't remove last element from an empty collection: 
array1.removeLast()
```

### 1.4 访问数组

使用下标法访问`Array`中的元素

```swift
array1[0]

array1[1] = 12

// 下标法可以使用区间运算符
array1[1...3] = [2,3,4]
```

### 1.5 遍历数组

可以直接使用`for-in`循环遍历数组

```swift
for value in array1 {
    print("\(value)")
}
```

如果同时需要每个数据项的值和索引值，可以使用 `enumerated()` 方法来进行数组遍历。`enumerated()` 返回一个由索引值和数据值组成的元组数组。

```swift

for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")
}

```

### 1.6 数组越界问题

> 如果你试图通过越界索引来执行访问或者修改数据的操作，会引发一个运行时错误。此时可以使用索引值和数组的 count 属性进行比较来在使用该索引之前检验其是否有效。除了当 count 等于 0 时（说明这是个空数组），最大索引值一直是 count - 1，因为数组都是零起索引。

## 2. 数组自动扩容

每个数组都会预留一定数量的内存空间去保存元素。当在数组中添加元素且超出预留的数量；数组会申请一块更大的内存空间，并将元素复制到新的内存空间中。

新的内存空间是旧空间的数倍大小。指数级的增长策略意味着多次append操作的平均性能是实时的。触发内存充分配的append 操作有一定的性能消耗，但是数组的容量的逐渐增大，内存分配将会发生的更少。

如果预先知道需要存储的元素数量，可以在添加元素之前调用`reserveCapacity(_:)` 避免内存重分配。`capacity` 和 `count` 属性可以用于计算可用容量。

对于大部分元素类型，存储区域是一个连续的内存空间。对于元素类型是 class 或者 @objc protocol 的数组，存储区域可以是一个连续的内存空间或者NSArray的对象。

## 3. 修改Array 的拷贝

Array 是struct类型，也就是值类型。Array对象的每一个拷贝之间都是独立的，修改一个拷贝并不会影响其他拷贝。

如果 Array 的元素类型是值类型，那不仅是Array对象的每个拷贝是独立的，Array对象的每个拷贝对应的元素也是独立的，修改某个拷贝的元素不会影响其他拷贝的对应元素。

```swift
var numbers = [1, 2, 3, 4, 5]
var numbersCopy = numbers
numbers[0] = 100
print(numbers)
// Prints "[100, 2, 3, 4, 5]"
print(numbersCopy)
// Prints "[1, 2, 3, 4, 5]"
```

如果 Array 的元素类型是引用类型，尽管Array对象的每个拷贝是独立的，Array对象的每个拷贝对应的元素是浅拷贝，指向同一个引用类型对象。

```swift
// An integer type with reference semantics
class IntegerReference {
    var value = 10
}
var firstIntegers = [IntegerReference(), IntegerReference()]
var secondIntegers = firstIntegers

// Modifications to an instance are visible from either array
firstIntegers[0].value = 100
print(secondIntegers[0].value)
// Prints "100"

// Replacements, additions, and removals are still visible
// only in the modified array
firstIntegers[0] = IntegerReference()
print(firstIntegers[0].value)
// Prints "10"
print(secondIntegers[0].value)
// Prints "100"
```

标准库中的许多集合包括Array都使用了 **写时拷贝** 优化。 Array 的多个拷贝共享同一块存储空间，直到某一个拷贝被修改。

当Array的某个拷贝首次被修改时，会复制出一块新的独属于该拷贝的存储空间，然后再修改；之后再修改就不再有申请新的存储空间的开销了。

```swift
var numbers = [1, 2, 3, 4, 5]
var firstCopy = numbers
var secondCopy = numbers

// The storage for 'numbers' is copied here
numbers[0] = 100
numbers[1] = 200
numbers[2] = 300
// 'numbers' is [100, 200, 300, 4, 5]
// 'firstCopy' and 'secondCopy' are [1, 2, 3, 4, 5]

```

## 4. Array 和 NSArray 之间桥接

当你需要 Array 和 NSArray 对象之间相关转换，可以使用类型声明运算符(`as`)来实现桥接。桥接的前提是元素的类型必须是`class`或者`@objc protocol`或者`Foundation类型`。

有以下的示例：

`colors` 是 `Array<String>`的数组，元素类型为 `String`, `String` 是一个Foundation类型，可以桥接；

`moreColors` 是 `Array<String?>`, 元素类型为 `String?`, `String?` 本质上是枚举类型，不是`class`或者`@objc protocol`或者`Foundation类型`中任意一种，因此不可以桥接。

```swift


let colors = ["periwinkle", "rose", "moss"]
let moreColors: [String?] = ["ochre", "pine"]

let url = URL(fileURLWithPath: "names.plist")
(colors as NSArray).write(to: url, atomically: true)
// true

(moreColors as NSArray).write(to: url, atomically: true)
// error: cannot convert value of type '[String?]' to type 'NSArray'
```

#### 4.1 `class` 或者 `@objc protocol` 的桥接性能

当元素类型为 `class` 或者 `@objc protocol`, `Array` 桥接为 `NSArray`的时间复杂度和空间复杂度都是 O(1)

当元素类型为 `class` 或者 `@objc protocol`, `NSArray` 桥接为 `Array` 首先调用 `copy(with:)` 方法获得一个可变的拷贝数组，然后执行额外的Swift保存操作，将会消耗O(1)的时间复杂度。如果是 `NSMutableArray` 对象桥接为`Array` ，`copy(with:)`通常会在O(1)时间内返回同一个数组对象，否则copy的性能将是不确定的。如果`copy(with:)`返回同一个数组对象，`NSArray` 和 `Array` 共享同一块支持写实拷贝的存储空间。

#### 4.2 Foundation 中非class类型的桥接性能

当元素类型为Foundation中的非class类型, `Array` 桥接为 `NSArray`的时间复杂度和空间复杂度都是 O(n)

当元素类型是Foundation中的非class类型，`NSArray` 桥接为 `Array`会在 O(n) 的时间复杂度内将元素拷贝到一块连续存储中。
