Swift:使用count或isEmpty检查一个集合是否为空的教程

34 阅读2分钟

在Swift中,基本上有两种主要方式来检查一个给定的集合是否为空。我们可以检查集合的count 是否等于0 ,或者使用专用的isEmpty 属性。

乍看起来,这两种方式的行为方式是完全相同的。事实上,我们完全可以想象isEmpty 是使用count-check - 这样来实现的:

extension Collection {
    var isEmpty: Bool { count == 0 }
}

不过在现实中,事实证明,标准库对isEmpty 的默认实现是这样的:

extension Collection {
    var isEmpty: Bool { startIndex == endIndex }
}

然而,某些具体的集合类型提供了一个自定义的isEmpty 的实现,它更适合于该特定集合的工作方式--这就是事情变得有趣的地方,因为如果我们看一下Set 是如何实现该属性的,那么我们会发现它实际上使用与我们上面想象的完全相同的count-check:

extension Set {
    var isEmpty: Bool { count == 0 }
}

这里发生了什么?为了回答这个问题,让我们看一下count 属性的官方文档,其中包括以下一句话。

复杂度。如果集合符合RandomAccessCollection ,则为O(1);否则为O*(n*),其中n为集合的长度。

因此,事实证明,在某些集合上简单地访问count 属性会导致一个在时间复杂性上是O(n) 的操作。换句话说,要对集合中的所有元素进行一次完整的迭代。

这种集合的一个例子是几乎每个Swift程序都在使用的一个集合--String 。这是因为,在Swift中,一个字符串的count ,指的是该字符串所包含的人类可读字符的数量,但这些字符实际上是以UTF-8代码点的形式存储的。因此,由于这两种格式不一定有相同的长度,确定一个字符串的确切字符数实际上需要循环浏览其所有的UTF-8内容(这是一个O(n) 操作),以计算其startIndexendIndex 之间的确切距离。

extension String {
    var count: Int {
        distance(from: startIndex, to: endIndex)
    }
}

这就是为什么Collection 协议的默认实现isEmpty 检查startIndexendIndex 属性是否相等,而不是使用count ,因为访问这两个索引属性不需要进行任何实际计算。

因此,总结一下--使用isEmptycount == 0 是等同的吗?有时,是的(例如在处理Set 时),但有时(例如在String 的情况下),使用count 来确定一个集合是否为空是非常浪费的--考虑到整个集合将被循环,只是为了让我们能够随后检查该计数是否等于0

*我的建议是。*当你想检查一个集合是否为空时,总是使用isEmpty 。它读起来更好,更容易理解,而且总是超级快。只有当你对集合中的实际元素数感兴趣时才使用count