若是你往數組中添加不少元素,你可能會發現:使用reserveCapacity()
函數提早告訴 Swift 你須要多大的長度,代碼性能會更好。然而,對它的使用你須要格外當心,由於它也可能使你的代碼變得很是很是慢。程序員
首先,讓咱們看一下數組的存儲策略。若是你建立一個包含四個元素的數組,Swift 將會分配足夠的內存去存儲這僅有的4個元素。因此,數組的 count
和 capacity
都是4。算法
如今,你想去添加第五個元素。但數組並無足夠的長度去添加它,因此數組須要尋找一塊足夠存放五個元素的內存,而後將數組的4個元素拷貝過去,最後再拼接第五個元素。它的時間複雜度是O(n)。swift
爲了不不斷從新分配內存,Swift 對數組的容量採用了一種幾何增長模式(a geometric allocation pattern)。這是一種很是好的方式,它成倍的增長數組的容量避免屢次從新分配內存的問題。當你在容量爲4的數組中添加第五個元素的時候,Swift 將會將數組的長度增長爲 8 。每當你超出數組的長度範圍,它將會以3二、64等成倍的依次增長。數組
若是你知道你將要存儲512個元素,你可使用reserveCapacity()
函數來通知 Swift。而後 Swift 會馬上分配一塊能夠存儲512個元素的內存給數組,而不是建立一個小數組,再屢次從新分配內存。bash
示例:app
var randomNumbers = [Int]()
randomNumbers.reserveCapacity(512)
for _ in 1...512 {
randomNumbers.append(Int.random(in: 1...10))
}
複製代碼
因爲reserveCapacity()
的時間複雜度也是 O(n) ,因此你應該在數組爲空的時候調用它。dom
可是這有一個很是重要的點:你須要肯定你的數組增加策略比 Swift 的好。記住, Swift 使用幾何增加策略,因此調整數組尺寸的次數會隨着數組容量的增長而減小,這就意味着它會將時間複雜度平攤爲O(1)。函數
Tip:平攤(amortization): 是程序員選擇用來描述算法隨時間變化的行爲的術語。雖然append()
在不得不擴充數組的容量的時候時間複雜度是O(n) ;當你有足夠的容量存儲的時候,它的時間複雜度是O(1)。隨着數組容量的增大,O(1) 操做將大大超過O(n)操做。所以咱們能夠認爲append()
的時間複雜度是O(1)。性能
當append()
平攤爲一個常量運行時間的時候,reserveCapacity()
也是如此。以前的用法將會使你的代碼變得更慢而不是更快。ui
例如,假設咱們想要追蹤抽獎的幸運數字。咱們能夠從一個空數組開始:
var allLuckyNumbers = [Int]()
複製代碼
下一步,咱們能夠編寫一個可以選擇10個數字的函數,以便咱們能夠在本週的彩票中進行遊戲。這個函數知道咱們將要生成10個數字,咱們可使用reserveCapacity()
來肯定咱們的數組能夠存放10個數字。
func pickLuckyNumbers() {
let newSize = allLuckyNumbers.count + 10
allLuckyNumbers.reserveCapacity(newSize)
for _ in 1...10 {
allLuckyNumbers.append(Int.random(in: 0...50))
}
}
複製代碼
目前爲止還不錯:reserveCapacity()
的時間複雜度爲O(n),而且他分配了足夠的內存來存儲。
可是人算不如天算,你可能提早想去生成整年的幸運數字。
for _ in 1...52 {
pickLuckyNumbers()
}
複製代碼
這個循環也是O(n),如今你攤上事了:循環裏面嵌套循環,時間複雜度爲O(n²)。
即便使用reserveCapacity()
可讓你的代碼變快,可是在這種狀況下它將使你的代碼變慢:Swift 將會重複調整數組的容量去添加十多個元素。另外一方面,若是你移除了reserveCapacity()
, Swift將恢復其幾何增加策略,並最終分配比所需更多的容量。這將會比重複調整數組的容量快不少。
判斷該使用的哪個的方法很簡單:若是你調用reserveCapacity()
一次,那麼你就應該使用它,可是若是你可能會調用它屢次,那麼你應該實現本身的增加策略或者使用 Swift 的幾何增加策略。