1. 内建集合类型
1.1 数组
数组和和变形
- 使用 let 定义一个指向类实例的引用类型时,它保证的是这个引用永远不会发生变化,也就是说,你不能再给这个引用赋一个新的值,但是这个
引用所指向的对象却是可以改变的。 - 所有集合类型都使用了
“写时复制”这一技术,它能够保证只在必要的时候对数据进行复制。
数组变形
map
- 功能:对集合中的
每个元素进行转换,并返回一个新的包含转换结果的集合。 - 示例:假设有一个包含整数的数组,我们想将每个元素都乘以 2,然后得到一个新的包含结果的数组。
- 代码例子:
let numbers = [1, 2, 3, 4, 5]
let doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers)
// 输出: [2, 4, 6, 8, 10]
从这个例子中可以看出,map 函数是对数组中每个元素做了指定的改变(乘以 2)。
再举个例子,假设我们有一个包含学生姓名的数组,我们想将每个学生的姓名首字母提取出来并转换为大写字母,得到一个新的数组。这时可以使用 map 方法来完成这个操作。
let students = ["Alice", "Bob", "Charlie", "David"]
let initials = students.map { $0.prefix(1).uppercased() }
print(initials)
// 输出: ["A", "B", "C", "D"]
- 要注意像这样链式使用
map和filter会创建一个中间数组 (也就是map操作的结果),这个数组会被立即抛弃。对于我们这个小例子来说这不会有什么问题,但是对于大的集合类型或者更长的链式调用,这种额外的内存申请会对性能造成影响。我们可以通过在链开始时加入.lazy,来让所有的变形延迟发生,来避免这些中间数组。只有在最后,我们才需要将这个延迟集合类型转换回一般的数组。
let lazyFilter = (1..<10).lazy.map { $0 * $0 }.filter { $0 % 2 == 0 }
let filtered = Array(lazyFilter) // [4, 16, 36, 64]
compactMap
- 功能:对集合中的每个元素进行转换,并返回一个新的包含转换结果的集合,这和
map函数一样。与map不同的是,compactMap会自动过滤掉转换结果为nil的元素。 - 示例:假设有一个包含字符串的数组,我们想将每个字符串转换成整数,然后得到一个新的包含转换结果的数组,自动过滤掉不是数字的字符串。
- 代码例子:
let strings = ["1", "2", "3", "abc", "5"]
let numbers = strings.compactMap { Int($0) }
print(numbers)
// 输出: [1, 2, 3, 5]
因为 "abc" 不是数字,所以通过 Int($0) 转换为数字时会返回 nil,compactMap 会自动过滤掉返回 nil 的元素,因此最终输出结果为 [1, 2, 3, 5]。
flatMap
- 功能:对集合中的
每个元素进行转换,并返回一个新的包含转换结果的扁平化集合,扁平化集合的意思是如果数组有嵌套,它会把嵌套的数组元素拿出来统一合并成一个单一的集合。 - 示例:假设有一个包含数组的数组,我们想将其中的每个数组合并成一个一维的数组。
- 代码例子:
let arrays = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
let flattenedArray = arrays.flatMap { $0 }
print(flattenedArray)
// 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]
这个例子中,外层的数组内嵌套了三个子数组,flatMap 会把子数组拿出来铺平。 再举一个现实中的例子,假设我们有一批订单,每个订单中有一系列商品,现在我想把所有订单里的商品合并成一个数组,得到一个扁平化的结果。这时可以使用 flatMap 方法来完成这个操作。
// 订单对象
struct Order {
// 产品列表
let products: [String]
}
// 所有订单
let orders = [
Order(products: ["Apple", "Banana", "Orange"]),
Order(products: ["Milk", "Eggs"]),
Order(products: ["Bread", "Butter", "Jam"])
]
let allProducts = orders.flatMap { $0.products }
print(allProducts)
// 输出: ["Apple", "Banana", "Orange", "Milk", "Eggs", "Bread", "Butter", "Jam"]
filter
用于筛选出满足条件的元素,返回包含满足条件的元素的集合。
let numbers = [1, 2, 3, 4, 5]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // 输出 [2, 4]
reduce
用于将集合中的所有元素按照某种规则进行聚合,最终返回一个值。
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { $0 + $1 }
print(sum) // 输出 15
其中,0是初始值,$0是累加器,$1是当前元素。
类似函数reduce(into:),方法签名:
func reduce<Result>( into initialResult: __owned Result, _ updateAccumulatingResult: (inout Result, Element) throws -> Void ) rethrows -> Result
方法中闭包的参数使用了 inout 关键字,使得闭包内部可以直接修改结果值。这样可以避免不必要的内存分配和拷贝,因此在处理大量数据时,使用 reduce(into:) 方法可以提高性能。
forEach
遍历集合中的每个元素,但不同于 for-in 循环,它不能使用 break 或 continue。
let numbers = [1, 2, 3]
numbers.forEach { print($0) }
// 输出 1 2 3
sorted
用于对集合进行排序,可以根据自定义规则排序。
let numbers = [3, 1, 4, 1, 5]
let sortedNumbers = numbers.sorted { $0 < $1 }
print(sortedNumbers) // 输出 [1, 1, 3, 4, 5]
类似函数还有sort(by:),区别在于直接修改原集合。
first(where:)
返回第一个满足条件的元素,若没有则返回 nil
let numbers = [1, 2, 3, 4, 5]
let firstEven = numbers.first(where: { $0 == 5 })
print(firstEven) // 输出 5
类似的还有firstIndex(where:), lastIndex(where:), last(where:)
contains(where:)
用于判断集合中是否有元素满足条件,返回布尔值。
let numbers = [1, 3, 5, 7]
let containsEven = numbers.contains { $0 % 2 == 0 }
print(containsEven) // 输出 false
allSatisfy
用于检查集合中的所有元素是否满足指定条件
let numbers = [2, 4, 6]
let allEven = numbers.allSatisfy { $0 % 2 == 0 }
print(allEven) // 输出 true
dropFirst/dropLast
用于去除集合的第一个或最后一个元素,返回剩余部分。
let numbers = [1, 2, 3, 4, 5]
let withoutFirst = numbers.dropFirst()
print(withoutFirst) // 输出 [2, 3, 4, 5]
类似函数drop(while:),当条件为true时,丢弃元素;一旦不为true,返回其余的元素。