Articles, podcasts and news about Swift development, by John Sundell.

Using count vs isEmpty to check whether a collection contains any elements

Published on 11 Nov 2021
Discover page available: The Standard Library

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.