8

To be more specific: will the Linq extension method Any(IEnumerable collection, Func predicate) stop checking all the remaining elements of the collections once the predicate has yielded true for an item?

Because I don't want to spend to much time on figuring out if I need to do the really expensive parts at all:

if(lotsOfItems.Any(x => x.ID == target.ID))
    //do expensive calculation here

So if Any is always checking all the items in the source this might end up being a waste of time instead of just going with:

var candidate = lotsOfItems.FirstOrDefault(x => x.ID == target.ID)
if(candicate != null)
     //do expensive calculation here

because I'm pretty sure that FirstOrDefault does return once it got a result and only keeps going through the whole Enumerable if it does not find a suitable entry in the collection.

Does anyonehave information about the internal workings of Any, or could anyone suggest a solution for this kind of decision?

Also, a colleague suggested something along the lines of:

if(!lotsOfItems.All(x => x.ID != target.ID))

since this is supposed to stop once the conditions returns false for the first time but I'm not sure on that, so if anyone could shed some light on this as well it would be appreciated.

Jeroen
  • 60,696
  • 40
  • 206
  • 339
garglblarg
  • 576
  • 1
  • 8
  • 16
  • 4
    [The documentation for `Enumerable.Any()`](https://msdn.microsoft.com/en-us/library/vstudio/bb337697%28v=vs.100%29.aspx) explicitly states the answer to this: *The enumeration of source is stopped as soon as the result can be determined.* – Matthew Watson May 15 '15 at 12:50

5 Answers5

14

As we see from the source code, Yes:

 internal static bool Any<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
            foreach (T element in source) {
                if (predicate(element)) {
                    return true; // Attention to this line
                }
            }
            return false;
        }

Any() is the most efficient way to determine whether any element of a sequence satisfies a condition with LINQ.

also:a colleague suggested something along the lines of

if(!lotsOfItems.All(x => x.ID != target.ID)) since this is supposed to stop once the conditions returns false for the first time but i'm not sure on that, so if anyone could shed some light on this as well it would be appreciated :>]

All() determines whether all elements of a sequence satisfy a condition. So, the enumeration of source is stopped as soon as the result can be determined.

Additional note:
The above is true if you are using Linq to objects. If you are using Linq to Database, then it will create a query and will execute it against database.

Farhad Jabiyev
  • 26,014
  • 8
  • 72
  • 98
4

You could test it yourself: https://ideone.com/nIDKxr

public static IEnumerable<int> Tester()
{
    yield return 1;
    yield return 2;
    throw new Exception();
}

static void Main(string[] args)
{
    Console.WriteLine(Tester().Any(x => x == 1));
    Console.WriteLine(Tester().Any(x => x == 2));
    
    try
    {
        Console.WriteLine(Tester().Any(x => x == 3));
    }
    catch
    {
        Console.WriteLine("Error here");
    }
}

Yes, it does :-)

also:a colleague suggested something along the lines of

if(!lotsOfItems.All(x => x.ID != target.ID))

since this is supposed to stop once the conditions returns false for the first time but i'm not sure on that, so if anyone could shed some light on this as well it would be appreciated :>]

Using the same reasoning, All() could continue even if one of the element returns false :-) No, even All() is programmed correctly :-)

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
2

It does whatever is the quickest way of doing what it has to do.

When used on an IEnumerable this will be along the lines of:

foreach(var item in source)
  if(predicate(item))
    return true;
return false;

Or for the variant that doesn't take a predicate:

using(var en = source.GetEnumerator())
  return en.MoveNext();

When run against at database it will be something like

SELECT EXISTS(SELECT null FROM [some table] WHERE [some where clause])

And so on. How that was executed would depend in turn on what indices were available for fulfilling the WHERE clause, so it could be a quick index lookup, a full table scan aborting on first match found, or an index lookup followed by a partial table scan aborting on first match found, depending on that.

Yet other Linq providers would have yet other implementations, but generally the people responsible will be trying to be at least reasonably efficient.

In all, you can depend upon it being at least slightly more efficient than calling FirstOrDefault, as FirstOrDefault uses similar approaches but does have to return a full object (perhaps constructing it). Likewise !All(inversePredicate) tends to be pretty much on a par with Any(predicate) as per this answer.

Single is an exception to this

Update: The following from this point on no longer applies to .NET Core, which has changed the implementation of Single.

It's important to note that in the case of linq-to objects, the overloads of Single and SingleOrDefault that take a predicate do not stop on identified failure. While the obvious approach to Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) would be something like:

public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
  /* do null checks */
  using(var en = source.GetEnumerator())
    while(en.MoveNext())
    {
      var val = en.Current;
      if(predicate(val))
      {
        while(en.MoveNext())
          if(predicate(en.Current))
            throw new InvalidOperationException("too many matching items");
        return val;
      }
    }
  throw new InvalidOperationException("no matching items");
}

The actual implementation is something like:

public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    /* do null checks */
    var result = default(TSource);
    long tally = 0;
    for(var item in source)
        if(predicate(item))
        {
            result = item;
            checked{++tally;}
        }
    switch(tally)
    {
        case 0:
            throw new InvalidOperationException("no matching items");
        case 1:
            return result;
        default:
            throw new InvalidOperationException("too many matching items");
    }
}

Now, while successful Single will have to scan everything, this can mean that an unsucessful Single is much, much slower than it needs to (and can even potentially throw an undocumented error) and if the reason for the unexpected duplicate is a bug which is duplicating items into the sequence - and hence making it far larger than it should be, then the Single that should have helped you find that problem is now dragging away through this.

SingleOrDefault has the same issue.

This only applies to linq-to-objects, but it remains safer to do .Where(predicate).Single() rather than Single(predicate).

Community
  • 1
  • 1
Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
1

Any stops at the first match. All stops at the first non-match.

I don't know whether the documentation guarantees that but this behavior is now effectively fixed for all time due to compatibility reasons. It also makes sense.

usr
  • 168,620
  • 35
  • 240
  • 369
1

Yes it stops when the predicate is satisfied once. Here is code via RedGate Reflector:

    [__DynamicallyInvokable]
    public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (predicate == null)
        {
            throw Error.ArgumentNull("predicate");
        }
        foreach (TSource local in source)
        {
            if (predicate(local))
            {
                return true;
            }
        }
        return false;
    }
James Lucas
  • 2,452
  • 10
  • 15