Linq .Any VS .Exists - What's the difference?
在集合上使用LINQ,以下代码行之间的区别是什么?
1 | if(!coll.Any(i => i.Value)) |
和
1 | if(!coll.Exists(i => i.Value)) |
更新1
当我分解
更新2
有人知道为什么没有这个代码吗?
参见文档
list.exists(对象方法-msdn)
Determines whether the List(T) contains elements that match the conditions defined by the specified predicate.
这在.NET 2.0之后就存在了,所以在Linq之前。用于谓词委托,但lambda表达式向后兼容。另外,just list还有这个(甚至不是ilist)
IEnumerable.Any(扩展方法-msdn)
Determines whether any element of a sequence satisfies a condition.
这是.NET3.5中的一个新特性,使用func(tsource,bool)作为参数,因此它被用于lambda表达式和linq。
在行为上,它们是相同的。
区别在于any是在system.linq.Enumerable上定义的任何
exists似乎不是扩展方法。我猜Coll是
简而言之,这些方法基本上是相同的。一个比另一个更普遍。
- any也有一个不带参数的重载,它只查找可枚举的中的任何项。
- 存在没有这样的过载。
tldr;性能方面的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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")) { } |
测试列表生成器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } |
有10万条记录
" Any: 00:00:00.3770377 Exists: 00:00:00.2490249"
五百万记录
" Any: 00:00:00.0940094 Exists: 00:00:00.1420142"
1M记录
" Any: 00:00:00.0180018 Exists: 00:00:00.0090009"
对于500k,(我还翻转了它们被评估的顺序,看看是否没有与任何第一次运行相关联的附加操作。)
" Exists: 00:00:00.0050005 Any: 00:00:00.0100010"
有10万条记录
" Exists: 00:00:00.0010001 Any: 00:00:00.0020002"
似乎
编辑:对于5和10万条记录,我改变了它生成列表的方式,
新的测试机制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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:好的,为了消除生成测试数据的影响,我把它全部写到文件中,然后从文件中读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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")) { } } } |
10米
" 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"
500 K
" Any: 00:00:00.0120012 Exists: 00:00:00.0040004"
作为Matas对基准测试的回答的延续。
tl/dr:exists()和any()速度相同。
首先:使用秒表进行基准测试是不精确的(请参阅系列0ne的另一个类似主题的答案),但它远比日期时间更精确。
获得真正精确读数的方法是使用性能分析。但是,了解这两个方法的性能如何相互衡量的一种方法是执行两个方法的时间负载,然后比较每个方法的最快执行时间。这样一来,抖动和其他噪音给我们带来糟糕的读数并不重要(它的确如此),因为在某种意义上,两次执行都是"同样误导"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 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 Fastest 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; } |
在执行上述代码4次之后(依次执行1000个
1 2 3 4 5 6 7 8 9 10 11 | 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 |
有一点差别,但差别太小,不能用背景噪音来解释。我的猜测是,如果一个人做100000或100000个
当您纠正测量值(如上所述):Any和Exists,并添加Average时,我们将得到以下输出:
1 2 3 4 5 6 7 8 9 | 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. |
此外,只有当值为bool类型时,这才有效。通常这与谓词一起使用。通常使用任何谓词来确定是否有满足给定条件的元素。在这里,你只需要做一个从元素i到bool属性的映射。它将搜索值属性为真的"i"。一旦完成,该方法将返回true。