Array
声明
@frozen struct Array<Element>
概述
数组是应用程序中最常用的数据类型之一。您使用数组来组织应用程序的数据。具体来说,您使用 Array
类型来保存单一类型的元素,即数组的 Element
类型。数组可以存储任何类型的元素——从整数到字符串再到类。
Swift 使使用数组字面量在代码中创建数组变得很容易:只需用方括号将逗号分隔的值列表括起来。在没有任何其他信息的情况下,Swift 会创建一个包含指定值的数组,自动推断数组的 Element
类型。例如:
// An array of 'Int' elements
let oddNumbers = [1, 3, 5, 7, 9, 11, 13, 15]
// An array of 'String' elements
let streets = ["Albemarle", "Brandywine", "Chesapeake"]
您可以通过在声明中指定数组的Element
类型来创建一个空数组。例如:
// Shortened forms are preferred
var emptyDoubles: [Double] = []
// The full type name is also allowed
var emptyFloats: Array<Float> = Array()
如果您需要使用固定数量的默认值预初始化的数组,请使用 Array(repeating:count:)
初始化程序。
var digitCounts = Array(repeating: 0, count: 10)
print(digitCounts)
// Prints "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]"
访问数组值
当您需要对数组的所有元素执行操作时,请使用 for
- in
循环遍历数组的内容。
for street in streets {
print("I don't live on \(street).")
}
// Prints "I don't live on Albemarle."
// Prints "I don't live on Brandywine."
// Prints "I don't live on Chesapeake."
使用isEmpty
属性快速检查数组是否有任何元素,或使用count
属性查找数组中元素的数量。
if oddNumbers.isEmpty {
print("I don't know any odd numbers.")
} else {
print("I know \(oddNumbers.count) odd numbers.")
}
// Prints "I know 8 odd numbers."
使用 first
和 last
属性可安全访问数组的第一个和最后一个元素的值。如果数组为空,则这些属性为 nil
。
if let firstElement = oddNumbers.first, let lastElement = oddNumbers.last {
print(firstElement, lastElement, separator: ", ")
}
// Prints "1, 15"
print(emptyDoubles.first, emptyDoubles.last, separator: ", ")
// Prints "nil, nil"
您可以通过下标访问单个数组元素。非空数组的第一个元素始终位于索引零处。您可以使用从零到但不包括数组计数的任何整数为数组下标。使用等于或大于 count
的负数或索引会触发运行时错误。例如:
print(oddNumbers[0], oddNumbers[3], separator: ", ")
// Prints "1, 7"
print(emptyDoubles[0])
// Triggers runtime error: Index out of range
添加和删除元素
假设您需要存储已注册您正在教授的课程的学生姓名列表。在注册期间,您需要在学生添加和删除类时添加和删除姓名。
var students = ["Ben", "Ivy", "Jordell"]
要将单个元素添加到数组的末尾,请使用 append(_:)
方法。通过将另一个数组或任何类型的序列传递给 append(contentsOf:)
方法,同时添加多个元素。
students.append("Maxime")
students.append(contentsOf: ["Shakia", "William"])
// ["Ben", "Ivy", "Jordell", "Maxime", "Shakia", "William"]
您可以通过对单个元素使用 insert(_:at:)
方法并使用 insert(contentsOf:at:)
从另一个集合或数组字面量插入多个元素来在数组中间添加新元素。该索引和后面的索引处的元素被移回以腾出空间。
students.insert("Liam", at: 3)
// ["Ben", "Ivy", "Jordell", "Liam", "Maxime", "Shakia", "William"]
要从数组中删除元素,请使用 remove(at:)
、 removeSubrange(_:)
和 removeLast()
方法。
// Ben's family is moving to another state
students.remove(at: 0)
// ["Ivy", "Jordell", "Liam", "Maxime", "Shakia", "William"]
// William is signing up for a different class
students.removeLast()
// ["Ivy", "Jordell", "Liam", "Maxime", "Shakia"]
您可以通过将新值分配给下标来用新值替换现有元素。
if let i = students.firstIndex(of: "Maxime") {
students[i] = "Max"
}
// ["Ivy", "Jordell", "Liam", "Max", "Shakia"]
增加数组的大小
每个数组都会保留特定数量的内存来保存其内容。当您向数组添加元素并且该数组开始超出其保留容量时,该数组会分配更大的内存区域并将其元素复制到新存储中。新存储是旧存储大小的倍数。这种指数增长策略意味着追加一个元素在恒定时间内发生,平均许多追加操作的性能。触发重新分配的追加操作有性能成本,但随着阵列变大,它们发生的频率越来越低。
如果您知道大约需要存储多少元素,请在追加到数组之前使用reserveCapacity(_:)
方法以避免中间重新分配。使用capacity
和count
属性来确定数组可以存储多少元素而不分配更大的存储空间。
对于大多数Element
类型的数组,此存储是一个连续的内存块。对于 Element
类型为类或 @objc
协议类型的数组,此存储可以是连续的内存块或 NSArray
的实例。因为 NSArray
的任意子类都可以成为 Array
,所以在这种情况下不能保证表示或效率。
修改数组的副本
每个数组都有一个独立的值,包括其所有元素的值。对于整数和其他结构等简单类型,这意味着当您更改一个数组中的值时,该元素的值在数组的任何副本中都不会更改。例如:
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]"
如果数组中的元素是类的实例,则语义是相同的,尽管它们起初可能看起来不同。在这种情况下,存储在数组中的值是对位于数组外部的对象的引用。如果您更改对一个数组中的对象的引用,则只有该数组具有对新对象的引用。但是,如果两个数组包含对同一个对象的引用,您可以从两个数组中观察到该对象属性的变化。例如:
// 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"
与标准库中的所有 variable-size 集合一样,数组使用 copy-on-write 优化。阵列的多个副本共享相同的存储,直到您修改其中一个副本。发生这种情况时,正在修改的阵列将其存储替换为自身唯一拥有的副本,然后对其进行原地修改。有时会应用可以减少复制量的优化。
这意味着如果一个数组与其他副本共享存储,则对该数组的第一次变异操作会产生复制该数组的成本。作为其存储的唯一所有者的阵列可以就地执行变异操作。
在下面的示例中,创建了一个numbers
数组以及共享同一存储的两个副本。当原始numbers
数组被修改时,它会在进行修改之前制作其存储的唯一副本。对numbers
进行了进一步修改,而两个副本继续共享原始存储。
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]
Array 和 NSArray 之间的桥接
当您需要访问需要 NSArray
实例而不是 Array
中的数据的 API 时,请使用 type-cast 运算符 (as
) 来桥接您的实例。为了使桥接成为可能,数组的 Element
类型必须是类、@objc
协议(从 Objective-C 导入的协议或标有 @objc
属性的协议)或桥接到 Foundation 的类型类型。
以下示例显示如何将Array
实例桥接到NSArray
以使用write(to:atomically:)
方法。在此示例中,colors
数组可以桥接到 NSArray
,因为 colors
数组的 String
元素桥接到 NSString
。另一方面,编译器会阻止桥接 moreColors
数组,因为它的 Element
类型是 Optional<String>
,它会将 not
桥接到 Foundation 类型。
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'
如果数组的元素已经是类或 @objc
协议的实例,则从 Array
桥接到 NSArray
需要 O(1) 时间和 O(1) 空间;否则,需要 O(n
) 时间和空间。
当目标数组的元素类型是类或@objc
协议时,从NSArray
桥接到Array
首先调用数组上的copy(with:)
(Objective-C中的- copyWithZone:
)方法来获取不可变副本和然后执行额外的 Swift 簿记工作,需要 O(1) 时间。对于已经不可变的NSArray
的实例,copy(with:)
通常在 O(1) 时间内返回相同的数组;否则,复制性能未指定。如果 copy(with:)
返回相同的数组,则 NSArray
和 Array
的实例使用与 Array
的两个实例共享存储时使用的相同 copy-on-write 优化来共享存储。
当目标数组的元素类型是桥接到 Foundation 类型的非类类型时,从 NSArray
桥接到 Array
会在 O(n
) 时间内将元素的桥接副本复制到连续存储。例如,从NSArray
桥接到Array<Int>
会执行这样的复制。访问Array
实例的元素时不需要进一步的桥接。
可用版本
相关用法
- Swift Array enumerated()用法及代码示例
- Swift ArraySlice starts(with:)用法及代码示例
- Swift ArraySlice reduce(_:_:)用法及代码示例
- Swift Array allSatisfy(_:)用法及代码示例
- Swift Array removeFirst()用法及代码示例
- Swift ArraySlice prefix(through:)用法及代码示例
- Swift Array removeSubrange(_:)用法及代码示例
- Swift ArraySlice prefix(upTo:)用法及代码示例
- Swift Array max()用法及代码示例
- Swift Array prefix(through:)用法及代码示例
- Swift ArraySlice randomElement()用法及代码示例
- Swift ArraySlice map(_:)用法及代码示例
- Swift Array reduce(into:_:)用法及代码示例
- Swift ArraySlice reversed()用法及代码示例
- Swift Array sort()用法及代码示例
- Swift ArraySlice append(contentsOf:)用法及代码示例
- Swift Array suffix(_:)用法及代码示例
- Swift Array dropLast(_:)用法及代码示例
- Swift Array max(by:)用法及代码示例
- Swift Array +=(_:_:)用法及代码示例
- Swift ArraySlice removeSubrange(_:)用法及代码示例
- Swift Array +(_:_:)用法及代码示例
- Swift ArraySlice shuffled()用法及代码示例
- Swift ArraySlice suffix(from:)用法及代码示例
- Swift Array reverse()用法及代码示例
注:本文由纯净天空筛选整理自apple.com大神的英文原创作品 Array。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。