Swift中的数组

在Swift中, 最常用的集合类型非数组莫属了. 然而相较于Objective-C, Swift中数组有不小的变化.

基本的操作变化:

  • 想要迭代数组?
    for x in array

  • 想要迭代除了第一个元素以外的数组其余部分?
    for x in array.dropFirst()

  • 想要迭代除了最后 5 个元素以外的数组?
    for x in array.dropLast(5)

  • 想要列举数组中的元素和对应的下标?
    for (num, element) in collection.enumerated()

  • 想要寻找一个指定元素的位置?
    if let idx = array.index { someMatchingLogic($0) }

除此之外, Swift为方便数组操作还添加了一系列高阶函数.

* mapflatMap — 如何对元素进行变换

  • filter — 元素是否应该被包含在结果中

  • reduce — 如何将元素合并到一个总和的值中

  • sequence — 序列中下一个元素应该是什么?

  • forEach — 对于一个元素,应该执行怎样的操作

  • sortlexicographicComparepartition — 两个元素应该以怎样的顺序进行排列

  • indexfirstcontains — 元素是否符合某个条件

  • minmax — 两个元素中的最小/最大值是哪个

  • elementsEqualstarts — 两个元素是否相等

  • split — 这个元素是否是一个分割符

这几个高阶函数使用一个函数作为参数, 将重复切杂乱的的代码, 如遍历等操作给剥离出来. 同时, 函数的命名也是非常严谨且浅显的, 非常值得回味其中的奥妙.

不但如此, 利用Swift编译器的一些特性(待详细描述), 我们可以写出异常简洁直观的代码:

let numbers = Array.init(0..<10)

numbers.map { $0 * $0 }.filter{ $0 % 2 == 0 } // [0, 4, 16, 36, 64]

numbers.reduce(0, +) // 运算符也是函数, 所以可以这么写, 结果为 45

数组切片

Swift中还允许我们通过下标来获取特定范围内的元素:

    let streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"]
    let streetsSlice = streets[2 ..< streets.endIndex]
    print(streetsSlice)
    // Prints "["Channing", "Douglas", "Evarts"]"
    let index = streetsSlice.index(of: "Evarts")    // 4
    print(streets[index!])
    // Prints "Evarts"

这是通过对 subscript 进行重载实现的, RandomAccessCollection协议的扩展中给出了方法的声明

public subscript(bounds: Range<Self.Index>) -> RandomAccessSlice<Self> { get }

需要注意的是, 在上面的代码事例中, 获取 streetsSlice 实例关于 Evartsindex, 以及通过这个 index 获取的元素, 都是与开始的数组 streets 相匹配的. 所以, 可以猜测出 数组切片类型 ArraySlice子元素, 只不过是原数组的引用, 并通过两个属性 来记录其相对于原数组的 首尾偏移.

数组切片

值类型

与OC中的数组最大的不同, Swift中的 Array 是一个结构体类型, 本身具有不可变的特性, 即便我们可以使用 var 来声明一个可变的数组, 但是这个可变性只体现在变量本身上, 而不是指里面的值. 改变一个结构体变量的属性, 在概念上来说,和为整个变量赋值一个全新的结构体是等价的. 我们总是使用一个新的结构体, 并设置被改变的属性值, 然后用它替代原来的结构体.

而对于数组而言, 如果其中的元素是引用语义的实例(如类对象), 那么元素属性的变化 并不会对持有它的数组有什么影响, 然而如果元素是值语义的实例(值类型, 比如结构体), 那么当这个元素的属性发生改变时, 就会生成一个新的实例来替代该元素, 继而导致引用它的数组来生成一个新的实例数组代替.

举个例子:


struct Node {
    var value: Int = 0;
}

class TestClass {
    var nodes: [Node] = [] {
        didSet {
            print(nodes)
        }
    }

    func changeFirstNode(to value: Int) {
        if nodes.count > 0 {
            nodes[0].value = value;
        }
    }
}

let test = TestClass();
// 创建一个nodes的数组赋值给test实例, 触发一次nodes的didSet
// 输出内容为[Node(value: 0), Node(value: 1), Node(value: 2)]
test.nodes = [Node(value: 0), Node(value: 1), Node(value: 2)]

// 更改了数组中第0个node的value值, 由于node是结构体类型, 当其变量变化时, 为nodes[0]这个变量赋值了一个全新的结构体
test.changeFirstNode(to: 5)
// 而由于Array类型也是值类型, 所以也会生成一个全新的数组, 将self.nodes指向这个心的数组, 从而导致出发了nodes的didSet.

// 此时会输出didSet中的打印: [Node(value: 5), Node(value: 1), Node(value: 2)]

订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x