What is the BigO of Swift's String.count?

瘦欲@ 提交于 2020-07-29 03:19:45

问题


When swift uses String.count is it:

O(n) where each time we call it we iterate through the entire String in order to count it

or

O(1) where swift has previously stored the size of this array and simply accesses it.


回答1:


It is definitely O(n). From the Swift Book:

As a result, the number of characters in a string can't be calculated without iterating through the string to determine its extended grapheme cluster boundaries. If you are working with particularly long string values, be aware that the count property must iterate over the Unicode scalars in the entire string in order to determine the characters for that string.

This has a few implications, the biggest of which is integer subscripting (i.e. str[5]) is not available through the standard library. Internally, String uses ASCII or UTF-16 encoding (from Swift 5, it uses UTF-8 only). If the string only uses ASCII characters then count can be O(1) but ASCII only has 127 characters so consider this an exception rather than the rule.

NSString, on the other hand, always uses UTF-16 so accessing its length is O(1). And also keep in mind that NSString.length != String.count (try strings with emojis and you'll see).

As for your second question, it does not cache count for subsequent calls. Every call to count is thus O(n), even if the string has not changed. The code in the Foundation repo also confirms that.




回答2:


After failing to find documentation on this or being able to find this function within the source code I tested this myself using performance tests as described below. It assumed O(1) was possible based on PHP's Array being O(1). Swifts String.count function appears to be O(n).

Results

Is count cached when it's been called before now? (no)

I also tested to see if calling String.count once would cache it. By comparing results when count has already been called and when it has been stored to a variable to ensure it's not being stored prior to calling .count in our normal tests.

Tests

import XCTest

class CountTests: XCTestCase {

    func test100K() {
        let testString = String(repeating: "a", count: 100000)
        self.measure {
            _ = testString.count
        }
    }

    func test1000K() {
        let testString = String(repeating: "a", count: 1000000)
        self.measure {
            _ = testString.count
        }
    }

    func test10000K() {
        let testString = String(repeating: "a", count: 10000000)
        self.measure {
            _ = testString.count
        }
    }

    func test10000KCached() {
        let testString = String(repeating: "a", count: 10000000)
        _ = testString.count
        self.measure {
            _ = testString.count
        }
    }

    func test10000KStrong() {
        let testString = String(repeating: "a", count: 10000000)
        let count = testString.count
        self.measure {
            _ = count
        }
    }
}



回答3:


Looks like O(n) to me based on a quick Playground test.

for step in 1...10 {
    let length = step * 100000
    let string = String(repeating: "x", count: length)
    let start = Date()
    let stringLength = string.count
    let end = Date()
    print("Length: \(stringLength), time: \(end.timeIntervalSince(start))")
}

// Length: 100000, time: 0.00178205966949463
// Length: 200000, time: 0.00132298469543457
// Length: 300000, time: 0.00184988975524902
// Length: 400000, time: 0.00218689441680908
// Length: 500000, time: 0.00302803516387939
// Length: 600000, time: 0.00368499755859375
// Length: 700000, time: 0.0039069652557373
// Length: 800000, time: 0.00444602966308594
// Length: 900000, time: 0.0052180290222168
// Length: 1000000, time: 0.00539696216583252


来源:https://stackoverflow.com/questions/50568726/what-is-the-bigo-of-swifts-string-count

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!