什么是Swift的String.count的BigO?

时间:2018-05-28 14:37:53

标签: arrays swift big-o

当swift使用String.count时:

O(n)每当我们调用它时,我们遍历整个String以计算它

O(1)其中swift先前已存储此数组的大小并只是访问它。

3 个答案:

答案 0 :(得分:2)

绝对是O(n)。来自Swift Book

  

因此,如果不迭代字符串以确定其扩展的字形集群边界,则无法计算字符串中的字符数。如果您使用特别长的字符串值,请注意count属性必须遍历整个字符串中的Unicode标量,以确定该字符串的字符。

这有一些影响,其中最大的一个是整数下标(即str[5])无法通过标准库获得。在内部,String使用ASCII或UTF-16编码(这可能随时改变)。如果字符串仅使用ASCII字符,则count可以是O(1),但ASCII只有127个字符,因此请将此视为异常,而不是规则。

另一方面,

NSString始终使用UTF-16,因此访问其lengthO(1)。并且还要记住NSString.length != String.count(尝试使用表情符号,你会看到)。

至于你的第二个问题,它不会为后续调用缓存count。因此,count的每次调用都是O(n),即使字符串没有更改。 Foundation回购中的代码也证实了这一点。

答案 1 :(得分:1)

在找不到相关文档或能够在源代码中找到此函数后,我自己使用如下所述的性能测试对此进行了测试。假设基于PHP's Array为O(1)的O(1)是可能的。 Swifts String.count函数似乎是 O(n)

<强>结果

Unit Test Results

现在之前调用count时会缓存吗? (无)

我还测试过,看一次调用String.count是否会缓存它。通过比较已经调用count的时间以及将其存储到变量中的结果,以确保在我们的正常测试中调用.count之前没有存储它。

Caching 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
        }
    }
}

答案 2 :(得分:-1)

基于快速的游乐场测试看起来像O(n)。

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