Swift String.Index 与将字符串转换为数组

Swift String.Index vs transforming the String to an Array

在 swift 文档中,他们说他们使用 String.Index 来索引字符串,因为不同的字符可能占用不同的内存量。

但我看到很多人将字符串转换为数组 var a = Array(s) 这样他们就可以通过 int 索引而不是 String.Index (这绝对更容易)

所以我想自己测试是否所有unicode字符都完全相同:

let cafeA = "caf\u{E9}" // eAcute
let cafeB = "caf\u{65}\u{301}" // combinedEAcute

let arrayCafeA = Array(cafeA)
let arrayCafeB = Array(cafeB)

print("\(cafeA) is \(cafeA.count) character \(arrayCafeA.count)")
print("\(cafeB) is \(cafeB.count) character \(arrayCafeB.count)")
print(cafeA == cafeB)

print("- A scalar")
for scalar in cafeA.unicodeScalars {
    print(scalar.value)
}
print("- B scalar")
for scalar in cafeB.unicodeScalars {
    print(scalar.value)
}

这是输出:

café is 4 character 4
café is 4 character 4
true
- A scalar
99
97
102
233
- B scalar
99
97
102
101
769

果然,正如文档中提到的,字符串只是一个字符数组,然后字素簇在字符对象中向下,那么他们为什么不通过 int 对其进行索引呢? creating/using String.Index 实际上有什么意义?

在字符串中,字节表示是打包的,因此如果不从头遍历整个字符串,就无法知道字符边界在哪里。

当转换为数组时,这是一次遍历,结果是一个在内存中等距间隔的字符数组,这就是允许通过 Int 索引进行恒定时间下标的原因。重要的是,数组被保留下来,所以可以在同一个数组上完成许多下标操作,只需要遍历一次字符串的字节,用于初始解包。

可以使用下标扩展 String,下标通过 Int 对它进行索引,您会看到它经常出现在 SO 上,但这是不明智的。标准库程序员本可以添加它,但他们故意选择不这样做,因为它掩盖了每个索引操作都需要单独遍历字符串字节的事实,即 O(string.count)。突然间,像这样无害的代码:

for i in string.indices {
    print(string[i]) // Looks O(1), but is actually O(string.count)!
}

变成二次方。