438

Using LINQ on collections, what is the difference between the following lines of code?

if(!coll.Any(i => i.Value))

and

if(!coll.Exists(i => i.Value))

Update 1

When I disassemble .Exists it looks like there is no code.

Update 2

Anyone know why there is no code there for this one?

ITWeiHan
  • 5,074
  • 1
  • 8
  • 29
Anthony D
  • 10,227
  • 11
  • 43
  • 67
  • 10
    How does the code you compiled looks like? How did you disassemble? ildasm? What did you expect to find but didn't? – Meinersbur Feb 06 '10 at 14:54

6 Answers6

442

See documentation

List.Exists (Object method - MSDN)

Determines whether the List(T) contains elements that match the conditions defined by the specified predicate.

This exists since .NET 2.0, so before LINQ. Meant to be used with the Predicate delegate, but lambda expressions are backward compatible. Also, just List has this (not even IList)

IEnumerable.Any (Extension method - MSDN)

Determines whether any element of a sequence satisfies a condition.

This is new in .NET 3.5 and uses Func(TSource, bool) as argument, so this was intended to be used with lambda expressions and LINQ.

In behaviour, these are identical.

Graham
  • 6,577
  • 17
  • 55
  • 76
Meinersbur
  • 7,391
  • 1
  • 25
  • 28
  • 6
    I later made [a post in another thread](http://stackoverflow.com/a/21436464/1336654) where I listed all the Linq "equivalents" of the .NET 2 `List<>` instance methods. – Jeppe Stig Nielsen Dec 01 '15 at 08:44
  • Some answers here say that any() is slower than exists(). But in real world use cases you can often combine it with other Linq methods which will make it faster, e.g. `myIEnum.Where(a => String.Equals(a.sex, "male")).Any(a => String.Equals(a.name, "Joe"))`. You can't use where() with exists() like this. – volkit Oct 22 '20 at 12:57
205

The difference is that Any is an extension method for any IEnumerable<T> defined on System.Linq.Enumerable. It can be used on any IEnumerable<T> instance.

Exists does not appear to be an extension method. My guess is that coll is of type List<T>. If so Exists is an instance method which functions very similar to Any.

In short, the methods are essentially the same. One is more general than the other.

  • Any also has an overload which takes no parameters and simply looks for any item in the enumerable.
  • Exists has no such overload.
Michel Ayres
  • 5,357
  • 8
  • 55
  • 92
JaredPar
  • 673,544
  • 139
  • 1,186
  • 1,421
  • 13
    Well put (+1). List.Exists has been around since .Net 2 but only works for generic lists. IEnumerable.Any was added in .Net 3 as an extension that works on any enumerable collection. There's also similar members like List.Count, which is a property and IEnumerable.Count() - a method. – Keith May 18 '09 at 19:53
55

TLDR; Performance-wise Any seems to be slower (if I have set this up properly to evaluate both values at almost same time)

        var list1 = Generate(1000000);
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;
            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s +=" Any: " +end1.Subtract(start1);
            }

            if (!s.Contains("sdfsd"))
            {

            }

testing list generator:

private List<string> Generate(int count)
    {
        var list = new List<string>();
        for (int i = 0; i < count; i++)
        {
            list.Add( new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    new RNGCryptoServiceProvider().GetBytes(cryptoResult);
                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray())); 
        }

        return list;
    }

With 10M records

" Any: 00:00:00.3770377 Exists: 00:00:00.2490249"

With 5M records

" Any: 00:00:00.0940094 Exists: 00:00:00.1420142"

With 1M records

" Any: 00:00:00.0180018 Exists: 00:00:00.0090009"

With 500k, (I also flipped around order in which they get evaluated to see if there is no additional operation associated with whichever runs first.)

" Exists: 00:00:00.0050005 Any: 00:00:00.0100010"

With 100k records

" Exists: 00:00:00.0010001 Any: 00:00:00.0020002"

It would seem Any to be slower by magnitude of 2.

Edit: For 5 and 10M records I changed the way it generates the list and Exists suddenly became slower than Any which implies there's something wrong in the way I am testing.

New testing mechanism:

private static IEnumerable<string> Generate(int count)
    {
        var cripto = new RNGCryptoServiceProvider();
        Func<string> getString = () => new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    cripto.GetBytes(cryptoResult);
                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());

        var list = new ConcurrentBag<string>();
        var x = Parallel.For(0, count, o => list.Add(getString()));
        return list;
    }

    private static void Test()
    {
        var list = Generate(10000000);
        var list1 = list.ToList();
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;

            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s += " Any: " + end1.Subtract(start1);
            }

            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            if (!s.Contains("sdfsd"))
            {

            }
        }

Edit2: Ok so to eliminate any influence from generating test data I wrote it all to file and now read it from there.

 private static void Test()
    {
        var list1 = File.ReadAllLines("test.txt").Take(500000).ToList();
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;
            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s += " Any: " + end1.Subtract(start1);
            }

            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            if (!s.Contains("sdfsd"))
            {
            }
        }
    }

10M

" Any: 00:00:00.1640164 Exists: 00:00:00.0750075"

5M

" Any: 00:00:00.0810081 Exists: 00:00:00.0360036"

1M

" Any: 00:00:00.0190019 Exists: 00:00:00.0070007"

500k

" Any: 00:00:00.0120012 Exists: 00:00:00.0040004"

enter image description here

Matas Vaitkevicius
  • 49,230
  • 25
  • 212
  • 228
  • 3
    No discredit to you, but I'm feeling sceptical over these benchmarks. Look at the numbers: Every result has a recursion happening (3770377 : 2490249). At least for me, that's a sure sign something is not correct. I'm not a hundred percent sure on the maths here, but I think the odds of that recurring pattern happening is 1 in 999^999 (or 999! maybe?) per value. So the chance of it happening **8 times** in a row is infinitesimal. I think it's because you use [DateTime for benchmarking](http://stackoverflow.com/questions/28637/is-datetime-now-the-best-way-to-measure-a-functions-performance). – Jerri Kangasniemi Sep 27 '16 at 06:53
  • @JerriKangasniemi Repeating same operation in isolation should always take same amount of time, same goes for repeating it multiple times. What makes you say that it's DateTime? – Matas Vaitkevicius Sep 27 '16 at 06:57
  • Of course it does. The problem is still that it is hugely unlikely to take for example 0120012 seconds for 500k calls. And if it would be perfectly linear, thus explaining the numbers so nicely, 1M calls would've taken 0240024 seconds (twice as long), however that's not the case. 1M calls takes 58,(3)% longer than 500k and 10M takes 102,5% longer than 5M. So it's not a linear function and thus not really reasonable for the numbers to all recurse. I mentioned DateTime because I have experienced problems with it myself in the past, because of DateTime not using high precision timers. – Jerri Kangasniemi Sep 27 '16 at 07:13
  • 2
    @JerriKangasniemi Could I suggest you fix it and post an answer – Matas Vaitkevicius Sep 27 '16 at 07:27
  • 1
    If I'm reading your results correctly, You reported Any to be only about 2 to 3 times the speed of Exists. I don't see how the data even mildly supports your assertion that "It would seem Any to be slower by magnitude of 2". It's a little slower, sure, not orders of magnitude. – Suncat2000 Mar 01 '19 at 16:49
  • @Suncat2000 is correct. A magnitude of 2 would be approximately 100 times slower. – rory.ap Jul 11 '19 at 13:11
  • Would be good if you delete intermediate/wrong results (on top where one starts reading). The end is very interesting again. Even nice graph. Thank you! – Andreas Reiff Aug 17 '20 at 17:46
17

As a continuation on Matas' answer on benchmarking.

TL/DR: Exists() and Any() are equally fast.

First off: Benchmarking using Stopwatch is not precise (see series0ne's answer on a different, but similiar, topic), but it is far more precise than DateTime.

The way to get really precise readings is by using Performance Profiling. But one way to get a sense of how the two methods' performance measure up to each other is by executing both methods loads of times and then comparing the fastest execution time of each. That way, it really doesn't matter that JITing and other noise gives us bad readings (and it does), because both executions are "equally misguiding" in a sense.

static void Main(string[] args)
    {
        Console.WriteLine("Generating list...");
        List<string> list = GenerateTestList(1000000);
        var s = string.Empty;

        Stopwatch sw;
        Stopwatch sw2;
        List<long> existsTimes = new List<long>();
        List<long> anyTimes = new List<long>();

        Console.WriteLine("Executing...");
        for (int j = 0; j < 1000; j++)
        {
            sw = Stopwatch.StartNew();
            if (!list.Exists(o => o == "0123456789012"))
            {
                sw.Stop();
                existsTimes.Add(sw.ElapsedTicks);
            }
        }

        for (int j = 0; j < 1000; j++)
        {
            sw2 = Stopwatch.StartNew();
            if (!list.Exists(o => o == "0123456789012"))
            {
                sw2.Stop();
                anyTimes.Add(sw2.ElapsedTicks);
            }
        }

        long existsFastest = existsTimes.Min();
        long anyFastest = anyTimes.Min();

        Console.WriteLine(string.Format("Fastest Exists() execution: {0} ticks\nFastest Any() execution: {1} ticks", existsFastest.ToString(), anyFastest.ToString()));
        Console.WriteLine("Benchmark finished. Press any key.");
        Console.ReadKey();
    }

    public static List<string> GenerateTestList(int count)
    {
        var list = new List<string>();
        for (int i = 0; i < count; i++)
        {
            Random r = new Random();
            int it = r.Next(0, 100);
            list.Add(new string('s', it));
        }
        return list;
    }

After executing the above code 4 times (which in turn do 1 000 Exists() and Any() on a list with 1 000 000 elements), it's not hard to see that the methods are pretty much equally fast.

Fastest Exists() execution: 57881 ticks
Fastest Any() execution: 58272 ticks

Fastest Exists() execution: 58133 ticks
Fastest Any() execution: 58063 ticks

Fastest Exists() execution: 58482 ticks
Fastest Any() execution: 58982 ticks

Fastest Exists() execution: 57121 ticks
Fastest Any() execution: 57317 ticks

There is a slight difference, but it's too small a difference to not be explained by background noise. My guess would be that if one would do 10 000 or 100 000 Exists() and Any() instead, that slight difference would disappear more or less.

Community
  • 1
  • 1
  • Could I suggest you do 10 000 and 100 000 and 1000000, just to be methodical about this, also why min and not average value? – Matas Vaitkevicius Sep 27 '16 at 10:07
  • 2
    Minimum value is because I want to compare the fastest execution (=probably least amount of background noise) of each method. I might do it with more iterations, although it will be later (I doubt my boss wants to pay me for doing this instead of working through our backlog) – Jerri Kangasniemi Sep 27 '16 at 11:22
  • I have asked Paul Lindberg and he say's it's ok ;) in regards to minimum I can see your reasoning however more orthodox approach is to use average https://en.wikipedia.org/wiki/Algorithmic_efficiency#Practice – Matas Vaitkevicius Sep 27 '16 at 11:29
  • 11
    If the code you posted is the one you actually executed it is not surprising that you get similar results, as you call Exists in both measurements. ;) – Simon Touchtech Jan 17 '17 at 09:35
  • Heh, yeah, I saw that too now you say it. Not in my execution though. This was just meant as a stripped down concept of what I was comparing. :P – Jerri Kangasniemi Feb 10 '17 at 09:26
  • 1
    `Random r = new Random();` *really* needs to be pulled OUT of that `for` loop. – Jesse C. Slicer Jul 12 '19 at 16:30
6

When you correct the measurements - as mentioned above: Any and Exists, and adding average - we'll get following output:

Executing search Exists() 1000 times ... 
Average Exists(): 35566,023
Fastest Exists() execution: 32226 

Executing search Any() 1000 times ... 
Average Any(): 58852,435
Fastest Any() execution: 52269 ticks

Benchmark finished. Press any key.
jasmintmp
  • 95
  • 1
  • 4
4

Additionally, this will only work if Value is of type bool. Normally this is used with predicates. Any predicate would be generally used find whether there is any element satisfying a given condition. Here you're just doing a map from your element i to a bool property. It will search for an "i" whose Value property is true. Once done, the method will return true.

flq
  • 20,838
  • 4
  • 49
  • 72