tft每日頭條

 > 生活

 > swift 解析數據

swift 解析數據

生活 更新时间:2025-01-09 19:50:57

了解 swift 數組如何在底層工作,以及如何優化它們——使用 ContiguousArray、它們的比較等。

swift 解析數據(七爪源碼深入了解)1

  • _ContiguousArrayStorage - 為存儲元素分配内存,通過索引提供快速訪問。
  • _ArrayBridgeStorage - 一種允許您同時使用本機存儲和 NSArray 的抽象。
  • _ArrayBuffer<T> - 寫入時複制的實現。
  • Array<T> - 公共接口。

增加數組的大小

每個數組保留特定數量的内存來存儲其内容。 當您将項目添加到數組并且該數組開始超過其保留容量時,該數組會分配大量内存并将其項目複制到新的保管庫,其中新保管庫的大小與舊保管庫的許多大小相同。

swift 解析數據(七爪源碼深入了解)2

這種指數增長策略意味着追加一個元素在恒定時間内發生,平均許多追加操作的性能。 觸發重新分配的追加操作有性能成本,但随着陣列變大,它們發生的頻率越來越低。

示例:創建一個包含 10 個元素的數組。

Swift 會為這個數組分配足夠的容量來存儲這 10 個元素,所以 array.capacity 和 array.count 都等于 10。

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(array.capacity) // 10 print(array.count) // 10

讓我們添加 11 和 12 個元素。 數組沒有這個容量,所以它需要釋放空間——它會找到内存來存儲更多元素,将數組複制到那裡,然後添加 11 和 12 個元素。 這些插入的執行時間很複雜——O(n),其中 n 是數組中元素的數量。

array.append(11) array.append(12) print(array.capacity) // 20 print(array.count) // 12

因此,當您将 11 和 12 個元素添加到容量為 10 的數組時,swift 會創建一個大小為 20 的數組。當我們超過這個大小時,下一個容量将是 40,然後是 80,汗水 160,等等 上。

如果您知道大約要保存多少項目,請使用 reserveCapacity(_:)。 這将允許您避免中間重新分配。

使用容量和計數屬性來确定一個數組可以存儲多少元素而不分配大量資源。

ar stringArray = Array<String>() stringArray.reserveCapacity(128)

如果您的數組的 Element 類型是類或 @objc 協議,并且您不需要将數組橋接到 NSArray 或将數組傳遞給 Objective-C API,則使用 ContiguousArray 可能比 Array 更高效且性能更可預測。 如果數組的 Element 類型是結構體或枚舉,Array 和 ContiguousArray 的效率應該差不多。

swift 解析數據(七爪源碼深入了解)3

ContiguousArray 類型是一個專門的數組,它始終将其元素存儲在一個連續的内存區域中。 這與 Array 形成對比,後者可以将其元素存儲在内存的連續區域或 NSArrayinstance 中,如果其元素類型是類或 @objc 協議。

數組将連續存儲:

  • 在 Swift 中創建的數組總是連續存儲的;
  • 結構和枚舉的數組将始終連續存儲;
  • 沒有 Objective-C 運行時的平台(即非達爾文平台)上的數組總是連續存儲的;
  • 數組不會被連續存儲的唯一情況是它是否屬于類并且已從 NSArray 橋接
  • 即使那樣,在許多情況下,NSArray 将被連續存儲,并且可以免費或攤銷成本提供指針

Array 和 ContiguousArray 的比較

import Foundation protocol Possibles { init(repeating: Bool, count: Int) subscript(index: Int) -> Bool { get set } var count: Int { get } } extension Array: Possibles where Element == Bool {} extension ContiguousArray: Possibles where Element == Bool {} func lazySieveOfEratosthenes<Storage: Possibles>(makeStorage: () -> Storage) -> [Int] { var possibles = makeStorage() let limit = possibles.count - 1 return (2 ... limit).lazy.filter { i in // Lazy, so that `possibles` is updated before it is used. possibles[i] }.map { i in stride(from: i * i, through: limit, by: i).lazy.forEach { j in possibles[j] = false } return i }.reduce(into: [Int]()) { (result, next) in result.append(next) } } func forSieveOfEratosthenes<Storage: Possibles>(makeStorage: () -> Storage) -> [Int] { var possibles = makeStorage() let limit = possibles.count - 1 var result = [Int]() for i in 2 ... limit where possibles[i] { var j = i * i while j <= limit { possibles[j] = false j = i } result.append(i) } return result } func test(_ name: String, _ storageType: String, _ function: (Int) -> [Int]) { let start = Date() let result = function(100_000) let end = Date() print("\(name) \(storageType) biggest: \(result.last ?? 0) time: \(end.timeIntervalSince(start))") } test("for ", "Array ") { limit in forSieveOfEratosthenes { Array(repeating: true, count: limit 1) } } test("for ", "ContiguousArray") { limit in forSieveOfEratosthenes { ContiguousArray(repeating: true, count: limit 1) } } test("lazy ", "Array ") { limit in lazySieveOfEratosthenes { Array(repeating: true, count: limit 1) } } test("lazy ", "ContiguousArray") { limit in lazySieveOfEratosthenes { ContiguousArray(repeating: true, count: limit 1) } }

結果:

for Array biggest: 99991 time: 41.016937017440796 for ContiguousArray biggest: 99991 time: 40.648478984832764 lazy Array biggest: 99991 time: 3.3549970388412476 lazy ContiguousArray biggest: 99991 time: 3.5851539373397827

另一個:

for Array biggest: 99991 time: 41.801795959472656 for ContiguousArray biggest: 99991 time: 42.37710893154144 lazy Array biggest: 99991 time: 3.438219904899597 lazy ContiguousArray biggest: 99991 time: 3.4085270166397095

啟動是在具有以下配置的筆記本電腦上完成的:

  • macOS 蒙特雷(12.3 版)
  • 處理器 2.3 GHz 8 核 Intel core i9
  • 内存 16 GB DDR4
  • Xcode 版本 13.3.1 (13E500a),遊樂場

代碼示例取自here,我建議您查看整個線程。 如您所見,Array 并不總是比 ContiguousArray 慢。

帶類的數組

如果 Array 中的元素是類的實例,則語義是相同的,盡管它們起初看起來可能不同。 在這種情況下,存儲在數組中的值是對數組外對象的引用。 當您更改單個數組中的對象引用時,隻有該數組具有對新對象的引用。 然而,如果兩個數組包含對同一個對象的引用,您可以從兩個數組觀察到該對象屬性的變化。

class MyClass { var name = "Name" } var firstArray = [MyClass(), MyClass()] var secondArray = firstArray firstArray[0].name = "Another Name" print(firstArray[0].name) // "Another name" print(secondArray[0].name) // "Another name

  • 替換、添加和删除僅适用于操作适用的數組:

firstArray[0] = MyClass() print(firstArray[0].name) // "Name" print(secondArray[0].name) // "Another name

Big-O 算法複雜性與操作:

  • O (1) — 恒定時間(最快)。 通過索引訪問數組中的元素:

let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] array[3]

  • O(n log n) — 對數時間(比常數時間稍差)。 升序排序,标準函數:

var people = ["Sandra", "Mike", "James", "Donald"] people.sort()

  • O(n) - 線性時間(比對數複雜度稍差)。 使用标準函數 forEach 計算元素的總和:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] var sumOfNumbers = 0 numbers.forEach { sumOfNumbers = $0 } print(sumOfNumbers) // 55

  • O(n ²) — 二次時間(略差于 O (n log n)。遍曆 2D 數組:

var twoDimensionalArray = [["first one", "second one"], ["first two", "second two"], ["first third", "second third"]] for i in 0..<twoDimensionalArray.count { for n in 0..<twoDimensionalArray[i].count { print(twoDimensionalArray[i][n]) } }

結論

Swift 中的數組是一種很好的數據類型,用于組織有序集合,您可以在其中存儲各種元素,但請記住值和引用類型的語義。 您可以執行各種操作(添加、删除等),不僅可以使用 Array,還可以使用 ContiguousArray、NSArray、ArraySliceand 等。

謝謝閱讀。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved