原文:Why using isEmpty is faster than checking count == 0swift
若是你想要檢查一個數組,集合, 字符串或者是其餘的集合類型是空,你可能會寫以下代碼:數組
let name = ""
if name.count == 0 {
print("You're anonymous!")
}
複製代碼
而後,這段代碼能夠像這樣被更好的表達:ide
if name.isEmpty {
print("You're anonymous!")
}
複製代碼
使用isEmpty能更加清晰的去讀,運行也更快。spa
爲了瞭解對於字符串而言,爲何isEmpty比count == 0更快,咱們須要研究一下Swift中的字符串是如何工做的。設計
Swift的字符串是一種複雜的字符集合,多個符號可能會組合成一個字符給用戶看。舉個例子,英國國旗這個表情是由兩個不一樣的字符組成的: "G"和"B"。固然不是那些文字字符,而是當Unicode符號並排放置的時候,它們就會自動變成英國國旗。code
你能夠看看下面截圖中的操做: 它將區域指示符號G存儲在一個字符串中,區域指示符號B存儲在在第二個字符串中,而後將它們合併在一塊兒組成第三個字符串。就像你看到的這樣,它們分開看是被虛線環繞的G和B,可是合在一塊兒,它們就自動變成了英國國旗。orm
看看這些字符串的count值,三個都是1。所以,從原生字符的方面來看,它是兩個字符,可是從用戶方面來看,它就是一個字符: 一面英國國旗。他們不想意外的將旗幟分紅兩半。Swift就是被設計成阻止咱們將Unicode字符串意外打破,因此它會認爲emoji就是一個字符。cdn
讓這個事情更具迷惑性,在這種場景背後,每一個特殊字符G和B能夠用UTF-16表示成兩個值,或者用UTF-8表示成四個值。blog
因爲這種複雜性,它不能夠經過整數,在一個字符串中單一讀取字符,這也意味着下面這種代碼是不能被編譯的:索引
let name = "Guybrush Threepwood"
print(name[0])
複製代碼
那不是由於這樣的代碼是不能工做的。事實上,咱們能夠爲字符串添加下面的擴展:
extension String {
subscript(i: Int) -> Character {
return self[index(startIndex, offsetBy: i)]
}
}
複製代碼
然而,事情不是這麼簡單的。由於每一個字符串中的字符,可能存儲着一個值,兩個值,四個值,或者多是12個值。Swift不能提早知道第五個字符是什麼 - 咱們的擴展起始於第一個字符,由i個字符計算,直到找到咱們要求的字符。
這會成爲問題,由於你可能會嘗試寫下面的這段代碼,去輸出全部字符串中的字符。
for i in stride(from: 0, to: name.count, by: 2) {
print(name[i])
}
複製代碼
咱們說這個循環的時間複雜度是O(n)。因此,一個單一字符的字符串可能花費一秒鐘去遍歷,一個兩個字符的字符串可能花費2秒,一個三個字符的字符串可能花費3秒,而後以此類推 - 它是線性相關的。
然而咱們的字符串的下標擴展,也是O(n)複雜度的。結果就是,咱們如今獲得了一個O(n)的操做中又有一個O(n),而後就變成了O(n^2)的操做。使用上面的代碼,就意味着一個2個字符的字符串,須要花費4秒,一個3個字符的字符串,將會花費9秒,一個10個字符的字符串,將會花費100秒,以此類推。
因此,即便咱們的代碼看起來將會執行的相對較快,實際上可能會很是的慢。
既然你明白了Swift的字符串內部是如何工做的,那就讓咱們回到isEmpty vs count == 0。 就像你看到的,Swift隱藏了全部的複雜性: 一個用戶可見的字符多是一打底層的值組合而成的。因此咱們使用count去讀取字符串的長度的時候,Swift須要去遍歷全部的字符,以肯定這個字符串有多長。它從頭開始,一直計數到結束。
這就意味着,讀取一個字符串的count,是一個O(n)的操做: 若是你有一個空的字符串,它基本就是瞬時的,可是若是你有完整的莎士比亞做品集的話,它就會話費一些時間。
做爲對比,使用isEmpty在作完一個比較簡單的比較後,就能返回true或者false: 咱們的字符串的開始的索引是否等於結束索引?若是相等的話,那麼字符串就是空的,而後咱們就完成了。它不須要計算全部的字符。
如今這個特殊的問題不適用於數組,集合,字典,只適用於字符串,因此你能夠在其餘地方使用count == 0,而只在字符串中使用isEmpty。然而出於如下兩個緣由,我會謹慎對待這個選擇:
1. isEmpty是一直都比檢查特定的值來的清晰,一般用代碼表達你本身的意圖,是成功的一半。
2. 你能夠更加簡單的避免在字符串中使用count == 0,由於你能夠在你的代碼中移除全部的實例。
複製代碼
幸運的是,SwiftLint和SwiftFormat能夠爲你關注這個,由於他們有選擇性規則,能夠準確的檢測這種狀況。