使用count 还是isEmpty检查集合是否为空?
在Swift中,基本上有两种主要方法来检查给定的集合是否为空。我们可以检查集合的count
是否等于0
,也可以使用专用的isEmpty
属性。
起初,两者的行为方式似乎完全相同。事实上,将isEmpty
想象为使用count
检查实现是完全合理的——像这样:
extension Collection {
var isEmpty: Bool { count == 0 }
}
然而,实际上,事实证明,标准库对isEmpty
的默认实现看起来像这样:
extension Collection {
var isEmpty: Bool { startIndex == endIndex }
}
然而,某些具体的集合类型随后提供了isEmpty
的自定义实现,以更适合其特定的工作方式——这是事情变得有趣的地方,如果看看Set
如何实现该属性,那么会发现它确实使用了上面想象的完全相同的count
检查:
extension Set {
var isEmpty: Bool { count == 0 }
}
怎么回事呢? 为了回答这个问题,让我们看看count
属性的官方文件,其中包括以下句子:
复杂性:O(1)如果集合符合
RandomAccessCollection
;否则,O(n),其中n是集合的长度。
因此,事实证明,简单地访问某些集合上的count
属性会导致一个在时间复杂度方面为O(n)
的操作。 换句话说,需要遍历一遍集合的所有元素。
这种集合的一个例子是String
,几乎每个Swift程序都使用。在Swift中,字符串的count
是指字符串包含的人类可读字符的数量,这些字符实际上存储为UTF-8代码点。这两种格式不一定具有相同的长度,事实上,确定字符串的确切字符数确实需要遍历所有UTF-8内容(这是一个O(n)
操作),以计算其startIndex
和endIndex
之间的确切距离:
extension String {
var count: Int {
distance(from: startIndex, to: endIndex)
}
}
这就是为什么Collection
协议的isEmpty
默认实现是检查startIndex
和endIndex
属性是否相等,而不是使用count
,因为访问这两个索引属性不需要执行任何实际计算。
那么,总而言之——使用isEmpty
和count == 0
是等价的吗?有时是的(例如在处理Set
时),但有时(如在String
的情况下)不是, 后者存在令人难以置信的浪费——因为需要遍历整个集合,才可以检查该计数是否等于0
。
我的建议:始终使用isEmpty
检查集合是否为空。它读起来更好,更不言自明,而且总是超级快。仅当对集合中元素的实际数量感兴趣时,才使用count
计数。