In Swift, there are essentially two main ways to check whether a given collection is empty. We can either check if the collection’s count
is equal to 0
, or we can use the dedicated isEmpty
property.
At first, it might seem like those two ways are completely identical in terms of how they behave. In fact, it would be completely reasonable to imagine isEmpty
as being implemented using a count
-check — like this:
extension Collection {
var isEmpty: Bool { count == 0 }
}
In reality, though, it turns out that the standard library’s default implementation of isEmpty
looks like this:
extension Collection {
var isEmpty: Bool { startIndex == endIndex }
}
However, certain concrete collection types then provide a custom implementation of isEmpty
that’s more tailored to how that specific collection works — and here’s where things get interesting, because if we take a look at how Set
implements that property, then we’ll find that it does actually use the exact same count
-check that we were imagining above:
extension Set {
var isEmpty: Bool { count == 0 }
}
What’s going on here? To answer that question, let’s take a look at the count
property’s official documentation, which includes the following sentence:
Complexity: O(1) if the collection conforms to
RandomAccessCollection
; otherwise, O(n), where n is the length of the collection.
So it turns out that simply accessing the count
property on certain collections results in an operation that’s O(n)
in terms of time complexity. In other words, a complete iteration through all of the collection’s elements is performed.
An example of such a collection is one that almost every single Swift program uses — String
. That’s because, in Swift, a string’s count
refers to the number of human-readable characters that the string contains, but those characters are actually stored as UTF-8 code points. So, since those two formats don’t necessarily have the same length, determining a string’s exact character count does, in fact, require it to loop through all of its UTF-8 contents (which is an O(n)
operation) in order to calculate the exact distance between its startIndex
and endIndex
:
extension String {
var count: Int {
distance(from: startIndex, to: endIndex)
}
}
That’s why the Collection
protocol’s default implementation of isEmpty
checks whether the startIndex
and endIndex
properties are equal, rather than using count
, since accessing those two index properties doesn’t require any actual computation to be performed.
So, to sum up — is using isEmpty
and count == 0
equivalent? Sometimes, yes (for example when working with a Set
), but sometimes (like in the case of String
), using count
to determine whether a collection is empty is incredibly wasteful — given that the entire collection will be looped through, just so that we can then check if that count is equal to 0
.
My recommendation: Always use isEmpty
when you want to check whether a collection is or isn’t empty. It reads better, is more self-explanatory, and is always super fast. Only use count
when you’re interested in the actual number of elements in the collection.