原文: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能够为你关注这个,由于他们有选择性规则,能够准确的检测这种状况。