tags:

views:

6029

answers:

12

What's the "best" (taking both speed and readability into account) way to determine if a list is empty? Even if the list is of type IEnumerable<T> and doesn't have a Count property.

Right now I'm tossing up between this:

if (myList.Count() == 0) { ... }

and this:

if (!myList.Any()) { ... }

My guess is that the second option is faster, since it'll come back with a result as soon as it sees the first item, whereas the second option (for an IEnumerable) will need to visit every item to return the count.

That being said, does the second option look as readable to you? Which would you prefer? Or can you think of a better way to test for an empty list?

Edit @lassevk's response seems to be the most logical, coupled with a bit of runtime checking to use a cached count if possible, like this:

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;

    return !list.Any();
}
+14  A: 

You could do this:

public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
    if (source == null)
        return true; // or throw an exception
    return !source.Any();
}

Edit: Note that simply using the .Count method will be fast if the underlying source actually has a fast Count property. A valid optimization above would be to detect a few base types and simply use the .Count property of those, instead of the .Any() approach, but then fall back to .Any() if no guarantee can be made.

Lasse V. Karlsen
Or use one line and do return (source==null) ? true : !source.Any(); (If your not throwing an exception)
Gage
+4  A: 

I just wrote up a quick test, try this:

 IEnumerable<Object> myList = new List<Object>();

 Stopwatch watch = new Stopwatch();

 int x;

 watch.Start();
 for (var i = 0; i <= 1000000; i++)
 {
    if (myList.Count() == 0) x = i; 
 }
 watch.Stop();

 Stopwatch watch2 = new Stopwatch();

 watch2.Start();
 for (var i = 0; i <= 1000000; i++)
 {
     if (!myList.Any()) x = i;
 }
 watch2.Stop();

 Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString());
 Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString());
 Console.ReadLine();

The second is almost three times slower :)

Trying the stopwatch test again with a Stack or array or other scenarios it really depends on the type of list it seems - because they prove Count to be slower.

So I guess it depends on the type of list you're using!

(Just to point out, I put 2000+ objects in the List and count was still faster, opposite with other types)

crucible
`Enumerable.Count<T>()` has special handling for `ICollection<T>`. If you try this with something *other* than a basic list, I expect you'll see *significantly* different (slower) results. `Any()` will remain about the same, though.
Marc Gravell
I have to concur with Marc; this isn't a really fair test.
Dan Tao
+1  A: 

@Crucible's profiling code yields some interesting results for different implementations of the list.

I like @lassevk's flexible extension-method approach. Certainly myList.IsEmpty() is the most readable syntax.

I'll leave this question open for a while to garner more opinions. I think it's an interesting question.

Matt Hamilton
A: 

The second option is much quicker if you have multiple items.

  • Any() returns as soon as 1 item is found.
  • Count() has to keep going through the entire list.

For instance suppose the enumeration had 1000 items.

  • Any() would check the first one, then return true.
  • Count() would return 1000 after traversing the entire enumeration.

This is potentially worse if you use one of the predicate overrides - Count() still has to check every single item, even it there is only one match.

You get used to using the Any one - it does make sense and is readable.

One caveat - if you have a List, rather than just an IEnumerable then use that list's Count property.

Keith
+1  A: 

@Keith The differences between Any() and Count() seem clear, but @crucible's profiling code seems to indicate that Count() is faster for certain implementations of IEnumerable. For List I can't get Any() to give a faster result than Count() until the list size is up in the thousands of items.

LINQ itself must be doing some serious optimization around the Count() method somehow.

Matt Hamilton
+4  A: 

LINQ itself must be doing some serious optimization around the Count() method somehow.

Does this surprise you? I imagine that for IList implementations, Count simply reads the number of elements directly while Any has to query the IEnumerable.GetEnumerator method, create an instance and call MoveNext at least once.

/EDIT @Matt:

I can only assume that the Count() extension method for IEnumerable is doing something like this:

Yes, of course it does. This is what I meant. Actually, it uses ICollection instead of IList but the result is the same.

Konrad Rudolph
+2  A: 

@Konrad what surprises me is that in my tests, I'm passing the list into a method that accepts IEnumerable<T>, so the runtime can't optimize it by calling the Count() extension method for IList<T>.

I can only assume that the Count() extension method for IEnumerable is doing something like this:

public static int Count<T>(this IEnumerable<T> list)
{
    if (list is IList<T>) return ((IList<T>)list).Count;

    int i = 0;
    foreach (var t in list) i++;
    return i;
}

... in other words, a bit of runtime optimization for the special case of IList<T>.

/EDIT @Konrad +1 mate - you're right about it more likely being on ICollection<T>.

Matt Hamilton
A: 

This extension method works for me:

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}
Jonny Dee
Avoid such use of exceptions. In the above code, you *expect* an exception for certain, well-defined inputs (i.e. empty enumerations). Hence, they are no exceptions, they are the rule. That’s an abuse of this control mechanism which has implications on readability and performance. Reserve the use of exceptions for truly exceptional cases.
Konrad Rudolph
Generally, I would agree. But this is a workaround for a corresponding missing IsEmpty method. And I would argue that a workaround never is the ideal way to do something... Furthermore, especially in this case, the intent is very clear and the "dirty" code is encapsulated and hidden away in a well-defined place.
Jonny Dee
-1: If you want to do it this way, use FirstOrDefault(), as in ChulioMartinez's answer.
Daniel Rose
+1  A: 

Ok, so what about this one?

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    return !enumerable.GetEnumerator().MoveNext();
}

EDIT: I've just realized that someone has sketched this solution already. It was mentioned that the Any() method will do this, but why not do it yourself? Regards

Jonny Dee
Yeah that's quite succinct.
Matt Hamilton
BUT it becomes less succinct when you properly enclose it in a `using` block since otherwise you've constructed an `IDisposable` object and then abandoned it. Then, of course, it become *more* succinct when you utilize the extension method that's already there and just change it to `return !enumerable.Any()` (which does precisely this).
Dan Tao
A: 

Another idea:

if(enumerable.FristOrDefault() != null)

However I like the Any() approach more.

ChulioMartinez
John K
A: 

List.Count is O(1) according to Microsoft's documentation:
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

so just use List.Count == 0 it's much faster than a query

This is because it has a data member called Count which is updated any time something is added or removed from the list, so when you call List.Count it doesn't have to iterate through every element to get it, it just returns the data member.

Dasmowenator
+1  A: 

I would make one small addition to the code you seem to have settled on: check also for ICollection, as this is implemented even by some non-obsolete generic classes as well (i.e., Queue<T> and Stack<T>). I would also use as instead of is as it's more idiomatic and has been shown to be faster.

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
    }

    var genericCollection = list as ICollection<T>;
    if (genericCollection != null)
    {
        return genericCollection.Count == 0;
    }

    var nonGenericCollection = list as ICollection;
    if (nonGenericCollection != null)
    {
        return nonGenericCollection.Count == 0;
    }

    return !list.Any();
}
Dan Tao