當前位置:
首頁 > 知識 > Swift 中 String 取下標及性能問題

Swift 中 String 取下標及性能問題

取下標String

String 用 String.Index 取下標(subscript)得到 Character,String.Index 要從 String 中獲取

let greeting = "Guten Tag!"
greeting[greeting.startIndex] // Character "G"
greeting[greeting.index(before: greeting.endIndex)] // Character "!"
greeting[greeting.index(after: greeting.startIndex)] // Character "u"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // Character "a"

String 用 Range 或 ClosedRange (以下 Range 和 ClosedRange 統稱為 Range) 取下標得到 String

let str = "abc"
str[str.startIndex..

Character

String 通過 characters 屬性獲得 String.CharacterView,表示屏幕上顯示的內容。String.CharacterView 通過 String.CharacterView.Index 取下標得到 Character,String.CharacterView.Index 要從 String.CharacterView 中獲取

let str = "abc"
let characters = str.characters // String.CharacterView
characters[characters.startIndex] // Character "a"

注意,String.CharacterView 不遵循 RandomAccessCollection 協議,用 String.CharacterView.Index 取下標不可以隨機訪問。另外,String.CharacterView.Index 與 String.Index 是相同的類型,屬於 Struct。String.Index 的文檔在 String 文檔下

typealias Index = String.CharacterView.Index

String.CharacterView 通過 Range 得到 String.CharacterView。用 Character 和 String.CharacterView 都可以生成 String

let str = "abc"
let characters = str.characters // String.CharacterView
let characters2 = characters[characters.startIndex..

用 String.CharacterView 生成 Array,可以用 Int、Range 取下標。用 Array 也可以生成 String

let str = "abc"
let arr = Array(str.characters) // Array ["a", "b", "c"]
arr[1] // Character "b"
arr[1...2] // ArraySlice ["b", "c"]
String(arr) // String "abc"

Character 可以直接與 "a" 比較

let str = "abc"
let a = str[str.startIndex] // Character "a"
let b = str[str.index(str.startIndex, offsetBy: 1)] // Character "b"
a == "a" // true
b > "a" // true

UTF-8

String 通過 utf8 屬性獲得 String.UTF8View,表示 UTF-8 編碼的內容。String.UTF8View 通過 String.UTF8View.Index 取下標得到 UTF8.CodeUnit,實際上是 UInt8;通過 Range 取下標得到 String.UTF8View。String.UTF8View.Index 要從 String.UTF8View 中獲取。String.UTF8View 不遵循 RandomAccessCollection 協議,用 String.UTF8View.Index 取下標不可以隨機訪問。用 String.UTF8View 生成 Array,可以用 Int、Range 取下標。用 String.UTF8View 可以生成 String。用 UInt8 或 Array 也可以生成 String,但內容表示數字或數字數組,不是數字的 UTF-8 編碼內容。

let str = "abc"
let utf8 = str.utf8 // String.UTF8View
let n = utf8[utf8.startIndex] // UInt8 97
let a = utf8[utf8.startIndex.. [97, 98, 99]
let n2 = arr[0] // UInt8 97
let arr2 = arr[0...1] // // ArraySlice [97, 98]

String 通過 utf8CString 屬性獲得 ContiguousArray,實際上是 ContiguousArray,表示 UTF-8 編碼的內容並且末尾增加一個 0,所以長度比 utf8 屬性的長度大 1。ContiguousArray 可以用 Int、Range 取下標,分別得到 Int8 和 ArraySliceContiguousArray 遵循 RandomAccessCollection 協議,用 Int 取下標可以隨機訪問。

let str = "abc"
let utf8 = str.utf8CString // ContiguousArray [97, 98, 99, 0]
let a = utf8[0] // Int8 97
let ab = utf8[0...1] // ArraySlice [97, 98]

UTF-16

String 通過 utf16 屬性獲得 String.UTF16View,表示 UTF-16 編碼的內容。String.UTF16View 通過 String.UTF16View.Index 取下標得到 UTF16.CodeUnit,實際上是 UInt16;通過 Range 取下標得到 String.UTF16View。String.UTF16View.Index 要從 String.UTF16View 中獲取。String.UTF16View 遵循 RandomAccessCollection 協議,用 String.UTF16View.Index 取下標可以隨機訪問。用 String.UTF16View 生成 Array,可以用 Int、Range 取下標。用 String.UTF16View 可以生成 String。用 UInt16 或 Array 也可以生成 String,但內容表示數字或數字數組,不是數字的 UTF-16 編碼內容。

let str = "abc"
let utf16 = str.utf16 // String.UTF16View
let n = utf16[utf16.startIndex] // UInt16 97
let a = utf16[utf16.startIndex.. [97, 98, 99]
let n2 = arr[0] // UInt16 97
let arr2 = arr[0...1] // // ArraySlice [97, 98]

性能對比

對 String、String.CharacterView、Array、String.UTF8View、Array、ContiguousArray、String.UTF16View、Array 進行判空(isEmpty)、獲取長度(count)、一個位置的取下標([index])、一段距離的取下標([range])測試,統計執行時間。

定義測試類型、列印和更新時間的方法、要測試的 String

import Foundation

enum TestType {
case isEmpty
case count
case index
case range
}

func printAndUpdateTime(_ date: inout Date) {
let now = Date
print(now.timeIntervalSince(date))
date = now
}

let s = "aasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafcpiluioufnlkqjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjliopjktyuljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasderwytwghfsdfsdfgfdsg vrutj7edbj7 fdgotuyoergcwhmkl5lknjklqawkyrcqjljkljqjlqjhbrlqwfcbhafci luioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcvcnvbwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjkn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg iopiouvrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkfghngdljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmbkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqasdfsdwkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljdqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasddfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbsdfdsrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfsadfsdgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqsdfasjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdafgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlkasdfsdfsdfgfdsg vrutj7edbj7 ergcwhmkl5lknjklqawkrcqjljkljqjlqjhbrlqwfcbhafciluioufnlkjvjakjn fnvjalgkhlkdkjlk"

測試代碼

let loopCount = 10000
let index = s.characters.count / 2
let testType: TestType = .range
print(testType)
var date = Date

forLoop: for _ in 0..

測試結果

判空

Swift 中 String 取下標及性能問題

獲取長度

Swift 中 String 取下標及性能問題

一個位置的取下標

Swift 中 String 取下標及性能問題

一段距離的取下標

Swift 中 String 取下標及性能問題

以上比較中,判斷 String 是否為空,訪問 String 的 isEmpty 速度最快。對於其他操作,遵循 RandomAccessCollection 協議(ContiguousArray、String.UTF16View 以及其他 Array)的類型效率較高。

進一步比較判空操作

let loopCount = 10000
var date = Date

for _ in 0..

Swift 中 String 取下標及性能問題

與訪問 String 的 isEmpty 相比,判斷 String 是否等於空 String 速度更快!

注意到文檔中,對 String.UTF8View 和 String.UTF16View 的 Range 取下標方法的說明

subscript(bounds: Range) -> String.UTF8View { get }
subscript(bounds: Range) -> String.UTF16View { get }

Complexity: O(n) if the underlying string is bridged from Objective-C, where n is the length of the string; otherwise, O(1).

如果 String 是從 Objective-C 的 NSString 橋接來的,時間複雜度為 O(n),否則為 O(1)。這句話怎麼理解呢?前面說了,String.UTF8View 不遵循 RandomAccessCollection 協議,而 String.UTF16View 遵循 RandomAccessCollection 協議,兩者的時間複雜度應該不同。這裡怎麼說時間複雜度與 String 是否橋接自 NSString 有關?以下進一步探究。

let s2 = NSString(string: s) as String

let loopCount = 10000
let index = s.characters.count / 2
let index2 = s.characters.count - 1

func test(_ s: String) {
var date = Date

let utf8 = s.utf8
for _ in 0..

測試結果

Swift 中 String 取下標及性能問題

對比 index 與 index2 的差異。測試參數 index2 約為 index 的 2 倍。UTF-8 index2 的耗時也約為 index 的 2 倍。UTF-16 的 index 和 index2 耗時相近。這與是否遵循 RandomAccessCollection 協議一致。

對比 String 與 NSString 的差異。橋接自 NSString 的 String 耗時比 String 要長,UTF-8 尤其明顯。這應該就是文檔說明的情況。用 Range 取下標,橋接自 NSString 的 String,比 String 多一些操作,多出 O(n) 級別的時間,而不是取下標的時間複雜度是 O(n)。

應用

具體應用時,選取哪種編碼方式、取下標方式?首先,編碼方式要看具體應用場景。編碼方法不同,字元串的長度可能不同。如果字元串只含英文,比較好辦。如果字元串含有中文或 Emoji,選擇編碼方式就要慎重。注意,NSString 的 length 屬性獲得的長度對應 UTF-16 編碼。

let str = "abc"
str.characters.count // 3
str.unicodeScalars.count // 3
str.utf16.count // 3
(str as NSString).length // 3
str.utf8.count // 3
str.utf8CString.count - 1 // 3
strlen(str) // 3

let emojiStr = "????"
emojiStr.characters.count // 1
emojiStr.unicodeScalars.count // 2
emojiStr.utf16.count // 4
(emojiStr as NSString).length // 4
emojiStr.utf8.count // 8
emojiStr.utf8CString.count - 1 // 8
strlen(emojiStr) // 8

let ChineseStr = "中文"
ChineseStr.characters.count // 2
ChineseStr.unicodeScalars.count // 2
ChineseStr.utf16.count // 2
(ChineseStr as NSString).length // 2
ChineseStr.utf8.count // 6
ChineseStr.utf8CString.count - 1 // 6
strlen(ChineseStr) // 6

一般情況下,字元串要顯示出來,就用 String.CharacterView。如果要取下標,考慮性能,就用 String.CharacterView 生成 Array。如果要用其他編碼方式,也可以生成相應的 Array,以 Int 或 Range 取下標,效率高而且代碼簡潔。

現在 LeetCode 支持 Swift 了。如果做 LeetCode 的需要多次進行字元串取下標的題目,用不遵循 RandomAccessCollection 協議的類型(例如 String.CharacterView、String.UTF8View),可能思路對了結果卻是 「Time Limit Exceeded」。參見:http://www.cnblogs.com/silence-cnblogs/p/6878223.html

為了避免 NSString 橋接帶來的性能問題,在 Swift 里盡量用 String;盡量減少 Objective-C 的代碼,盡量選擇 Swift 編寫的第三方庫。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

.Net Core中使用ref和Span提高程序性能
每天一道Java題「4」
MySQL 的性能(上篇)——SQL 執行時間分析
linux c++爬蟲(一)
Azure Event Hub 技術研究系列2-發送事件到Event Hub

TAG:科技優家 |

您可能感興趣

Salesforce 5大性能問題
VMware workstation的性能優化
在Salesforce Lightning Experience提高性能和速度
利用Skywalking-netcore監控你的應用性能
Red Hat:Meltdown和Spectre漏洞可能會影響性能
使用 BenchmarkDotnet 測試代碼性能
杉岩統一存儲推出SandStone AgileStore高性能引擎
Microsoft Launcher 發布 提升性能 修復崩潰問題
cephFS kernel client IO性能瓶頸分析
微服務網關哪家強?一文看懂Zuul, Nginx, Spring Cloud, Linkerd性能差異
BeatifulSoup,Xpath,CSS 選擇器的性能比較
性能怪獸!全新配色 Air Jordan 18 「Yellow Suede」 實物亮相
Volvo S60 T8 Polestar Engineeered,北極星性能轎跑!
高振頻與高性能的絕美展現:蕭邦Superfast Chrono Porsche 919 Black Edition計時腕錶
Galaxy S9樹立Android性能新標杆 但iPhone X仍然比它快
Nike Epic React Flyknit 性能分析與搭配
Facebook發布Tensor Comprehensions:自動編譯高性能機器學習核心的C+庫
Oculus修復Rift當機問題,微軟改進SteamVR遊戲性能
linux性能調試之iostat
iPhone 7 Plus和iPhone X,性能區別?