LINQ .Any VS .Exists - What's the difference?
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?
Solution 1:
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.
Solution 2:
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.
Solution 3:
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"