Which method performs better: .Any() vs .Count() > 0?

Viewed 206089

in the System.Linq namespace, we can now extend our IEnumerable's to have the Any() and Count() extension methods.

I was told recently that if i want to check that a collection contains 1 or more items inside it, I should use the .Any() extension method instead of the .Count() > 0 extension method because the .Count() extension method has to iterate through all the items.

Secondly, some collections have a property (not an extension method) that is Count or Length. Would it be better to use those, instead of .Any() or .Count()?

yea / nae?

11 Answers

If you are starting with something that has a .Length or .Count (such as ICollection<T>, IList<T>, List<T>, etc) - then this will be the fastest option, since it doesn't need to go through the GetEnumerator()/MoveNext()/Dispose() sequence required by Any() to check for a non-empty IEnumerable<T> sequence.

For just IEnumerable<T>, then Any() will generally be quicker, as it only has to look at one iteration. However, note that the LINQ-to-Objects implementation of Count() does check for ICollection<T> (using .Count as an optimisation) - so if your underlying data-source is directly a list/collection, there won't be a huge difference. Don't ask me why it doesn't use the non-generic ICollection...

Of course, if you have used LINQ to filter it etc (Where etc), you will have an iterator-block based sequence, and so this ICollection<T> optimisation is useless.

In general with IEnumerable<T> : stick with Any() ;-p

The exact details differ a bit in .NET Framework vs .NET Core, but it also somewhat depends on what you're doing: if you're using an ICollection or ICollection<T> type (such as with List<T>) there is a .Count property that's cheap to access, whereas other types might require enumeration.

TL;DR:

Use .Count > 0 if the property exists, and otherwise .Any().

Using .Count() > 0 is never the best option, and in some cases could be dramatically slower.

This applies to both .NET Framework and .NET Core.


Now we can dive into the details..

Lists and Collections

Let's start with a very common case: using List<T> (which is also ICollection<T>).

The .Count property is implemented as:

    private int _size;

    public int Count {
        get {
            Contract.Ensures(Contract.Result<int>() >= 0);
            return _size; 
        }
    }

What this is saying is _size is maintained by Add(),Remove() etc, and since it's just accessing a field this is an extremely cheap operation -- we don't need to iterate over values.

ICollection and ICollection<T> both have .Count and most types that implement them are likely to do so in a similar way.

Other IEnumerables

Any other IEnumerable types that aren't also ICollection require starting enumeration to determine if they're empty or not. The key factor affecting performance is if we end up enumerating a single item (ideal) or the entire collection (relatively expensive).

If the collection is actually causing I/O such as by reading from a database or disk, this could be a big performance hit.


.NET Framework .Any()

In .NET Framework (4.8), the Any() implementation is:

public static bool Any<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    using (IEnumerator<TSource> e = source.GetEnumerator()) {
        if (e.MoveNext()) return true;
    }
    return false;
}

This means no matter what, it's going to get a new enumerator object and try iterating once. This is more expensive than calling the List<T>.Count property, but at least it's not iterating the entire list.

.NET Framework .Count()

In .NET Framework (4.8), the Count() implementation is (basically):

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    ICollection<TSource> collection = source as ICollection<TSource>;
    if (collection != null)
    { 
        return collection.Count;
    }
    int num = 0;
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            num = checked(num + 1);
        }
        return num;
    }
}

If available, ICollection.Count is used, but otherwise the collection is enumerated.


.NET Core .Any()

The LINQ Any() implementation in .NET Core is much smarter. You can see the complete source here but the relevant bits to this discussion:

    public static bool Any<TSource>(this IEnumerable<TSource> source)
    {
        //..snip..
        
        if (source is ICollection<TSource> collectionoft)
        {
            return collectionoft.Count != 0;
        }
        
        //..snip..

        using (IEnumerator<TSource> e = source.GetEnumerator())
        {
            return e.MoveNext();
        }
    }

Because a List<T> is an ICollection<T>, this will call the Count property (and though it calls another method, there's no extra allocations).

.NET Core .Count()

The .NET Core implementation (source) is basically the same as .NET Framework (see above), and so it will use ICollection.Count if available, and otherwise enumerates the collection.


Summary

.NET Framework

  • With ICollection:

    • .Count > 0 is best
    • .Count() > 0 is fine, but ultimately just calls ICollection.Count
    • .Any() is going to be slower, as it enumerates a single item
  • With non-ICollection (no .Count property)

    • .Any() is best, as it only enumerates a single item
    • .Count() > 0 is bad as it causes complete enumeration

.NET Core

  • .Count > 0 is best, if available (ICollection)
  • .Any() is fine, and will either do ICollection.Count > 0 or enumerate a single item
  • .Count() > 0 is bad as it causes complete enumeration

If you are using the Entity Framework and have a huge table with many records Any() will be much faster. I remember one time I wanted to check to see if a table was empty and it had millions of rows. It took 20-30 seconds for Count() > 0 to complete. It was instant with Any().

Any() can be a performance enhancement because it may not have to iterate the collection to get the number of things. It just has to hit one of them. Or, for, say, LINQ-to-Entities, the generated SQL will be IF EXISTS(...) rather than SELECT COUNT ... or even SELECT * ....

Using Count() to test for emptiness works, but using Any() makes the intent clearer, and the code more readable. However, there are some cases where special attention should be paid:

if the collection is an EntityFramework or other ORM query, calling Count() will cause executing a potentially massive SQL query and could put a large overhead on the application database. Calling Any() will also connect to the database, but will generate much more efficient SQL.

if the collection is part of a LINQ query that contains Select() statements that create objects, a large amount of memory could be unnecessarily allocated. Calling Any() will be much more efficient because it will execute fewer iterations of the enumerable.

Example to use Any():

private static bool IsEmpty(IEnumerable<string> strings)
{
  return !strings.Any();
}

I have created a sample application using IList with 100 elements to 1 millions items to see Count vs Any which is best.

Code

class Program
{
    static void Main()
    {

        //Creating List of customers
        IList<Customer> customers = new List<Customer>();
        for (int i = 0; i <= 100; i++)
        {
            Customer customer = new Customer
            {
                CustomerId = i,
                CustomerName = string.Format("Customer{0}", i)
            };
            customers.Add(customer);
        }

        //Measuring time with count
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
        if (customers.Count > 0)
        {
            Console.WriteLine("Customer list is not empty with count");
        }
        stopWatch.Stop();
        Console.WriteLine("Time consumed with count: {0}", stopWatch.Elapsed);

        //Measuring time with any
        stopWatch.Restart();
        if (customers.Any())
        {
            Console.WriteLine("Customer list is not empty with any");
        }
        stopWatch.Stop();
        Console.WriteLine("Time consumed with count: {0}", stopWatch.Elapsed);
        Console.ReadLine();

    }
}

public class Customer
{
    public int CustomerId { get; set; }
    public string CustomerName { get; set; }
}

Result : enter image description here

Any is better than count.

Related