美文网首页
【Swift进阶笔记】内建集合类型-数组

【Swift进阶笔记】内建集合类型-数组

作者: BeethOven | 来源:发表于2021-10-30 22:00 被阅读0次
    image.png

    可变性

    • 数组和标准库中的所有集合类型一样,是具有值语义的
    var x = [1,2,3]
    var y = x
    y.append(4)
    y // [1, 2, 3, 4]
    x // [1, 2, 3]
    

    想要改变一个数组,必须使用NSMutableArray,

    但是NSArray的引用特性并不能保证这个数组不会被改变

    let a = NSMutableArray(array: ["1", "2", "3"])
    let b = a
    a.insert("4", at: 3)
    print(b)
    
    (
      1,
      2,
      3,
      4
    )
    

    需要进行复制, 因为a声明是可变的

    let c = NSMutableArray(array: ["1", "2", "3"])
    let d = c.copy() as! NSArray
    c.insert("4", at: 3)
    print(d)
    
    (
      1,
      2,
      3
    )
    

    “在 Swift 中,数组只有一种统一的类型,可以通过在声明时使用 var 而非 let 来将数组定义为可变的。当你使用 let 声明第二个数组,并将第一个数组赋值给它时,可以保证这个新的数组是不会改变的,因为这里没有共用的引用。”

    写实复制: 创建如此多的copy,会造成性能上的问题。所以引入了“写时复制”的技术。保证能够在必要的时候对数据进行复制。

    y在append之前x, y共享内部的存储

    索引

    Swift不鼓励去做索引计算,因为这样做可能会引起潜在bug.无效的下标会造成可控的崩溃, 数组没有可选值选项。

    • 迭代数组 for x in array

    • 迭代除了第一个数组 for x in array.dropFirst()

    • 迭代除了最后5个数组 for x in array.dropLast(5)

    • 列举元素和对应的下标 for(num, element) in array.enumerated

    • 寻找指定元素 if let index = array.firstIndex(where: { $0=="1" })

    • 数组元素变形array.map { return $0 + "1"}

    • 筛选出符合条件的元素array.filter { $0=="1" }

    下面操作略有不同

    • array.first(array.last 同理) 相当于
    isEmpty ? nil : array[0]
    
    • removeLast & popLast & dropLast

      • removeLast空数组将造成崩溃

      • popLast删除空数组时会返回nil,删除元素时,会返回该元素

      • dropLast删除空数组时会返回nil,删除元素时,会返回剩余元素数组

    变形

    Map

    var squared: [Int] = []
    for fib in fibs {
    squared.append(fib * fib)
    }
    squared // [0, 1, 1, 4, 9, 25]
    

    map完成的操作

    var squared: [Int] = []
    for fib in fibs {
    squared.append(fib * fib)
    }
    squared // [0, 1, 1, 4, 9, 25]
    

    优势

    • 长度短,代表错误少, 清晰

    • map相当于一个信号

    • map返回的数组用let声明

    Map函数内部可能实现方式

    extension Array {
    func map<T>(_ transform: (Element)->T) -> [T] {
        var result: [T] = []
        result.reserveCapacity(count)
        for x in self {
            result.append(transform(x))
        }
        return result
    }
    }
    

    Element是数组中包含元素类型的占位符,T是元素转换后的类型的占位符

    实际上这个函数签名应该是

    func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
    

    将函数行为参数化

    • map 和 CompactMap - 对元素进行变换

    • filter - 筛选

    • reduce - 将元素合并到一个总和的值

    • squence - 下一个元素

    • forEach - 对于一个元素执行怎样的操作

    • sort,lexicographicCompare 和 partition - 两个元素应该以怎样的顺序进行排列

    • first, firstIndex, contains - 元素是否符合某一个条件

    • elementsEqual和starts - 俩个元素是否相等

    • split - 这个元素是否是封割符

    • prefix - 判断为真,将元素过滤出来,一旦不为真,就将剩余结果抛弃,和filter类似,但是会提前退出。这个函数在处理无限序列或者延迟家孙(lazily-computed)的时候非常有用

    • drop - 判断为真,丢弃元素,一旦不为真,将剩余结果返回。和prefix(while:)相似,不过返回相反的集合

    Filter

    (1..<10).map { $0 * $0 }.filter { $0 % 2 == 0 } // [4, 16, 36, 64]
    

    实现源码

    extension Array {
      func filter(_ isIncluded: (Element) -> Bool) -> [Element] {
          var result: [Element] = []
          for x in self where isIncluded(x) {
          result.append(x)
          }
      return result
      }
    }
    

    对于判断条件filter的性能比contains低,因为contains不会因为计数而去全新创建一个数组,并且一旦找到第一个元素就会退出

    Reduce

    所有值合并为新值

    let sum = fibs.reduce(0) { total, num in total + num } // 12
    fibs.reduce(0, +) // 12
    

    输入的值和输出的值可以不同

    print(array.reduce("") { str, num in str + "\(num)," })
    // 0,1,1,2,3,5
    

    实现源码

    extension Array {
      func reduce<Result>(_ initialResult: Result,
                          _ nextPartialResult: (Result, Element) -> Result) -> Result
      {
          var result = initialResult
          for x in self {
              result = nextPartialResult(result, x)
          }
          return result
      }
    }
    

    Reduce实现map和filter

    extension Array {
      func map2<T>(_ transform: (Element) -> T) -> [T] {
          return reduce([]) {
              $0 + [transform($1)]
          }
      }
      func filter2(_ isIncluded: (Element) -> Bool) -> [Element] {
          return reduce([]) {
              isIncluded($1) ? $0 + [$1] : $0
          }
      }
    }
    

    For Each

    对集合中每个元素调用一个函数,return不能返回到函数的作用域之外,他做的仅仅是从闭包中返回

    array.forEach{
      print($0)
      if $0 > 2 {
          return
      }
      print($0)
    }
    0
    0
    1
    1
    1
    1
    2
    2
    3
    5
    

    切片

    通过下标获取元素

    let slice = array[1...]
    print(slice)
    print(type(of: slice))
    
    [1, 1, 2, 3, 5]
    ArraySlice<Int>
    

    切片类型ArraySlice<T>只是一种表示方式,它的背后仍然是原来的数组。因为数组的元素不会被复制,所以创建一个切片的代价是很小的。

    [图片上传失败...(image-8e753c-1635170375028)]

    ArraySlice和Array都满足了相同的协议(当中最重要的是Collection协议),所以可以把切片当数组处理

    let newArray = Array(slice)
    type(of: newArray) // Array<Int>
    

    如果是切片用startIndex 和 endIndex 属性做索引计算。

    相关文章

      网友评论

          本文标题:【Swift进阶笔记】内建集合类型-数组

          本文链接:https://www.haomeiwen.com/subject/igisaltx.html