Linq。所有VS.Exists – 有什么区别?
在集合上使用Linq,以下几行代码有什么不同?
if(!coll.Any(i => i.Value))
和
if(!coll.Exists(i => i.Value))
更新1
当我拆解。 .Exists
它看起来像没有代码。
更新2
任何人都知道为什么这里没有代码?
见文件
List.Exists (对象方法 – MSDN)
确定List(T)是否包含与由指定谓词定义的条件相匹配的元素。
这是从.NET 2.0开始的,所以在LINQ之前。 意味着与Predicate 委托一起使用,但是lambdaexpression式是向后兼容的。 此外,只是列表有这个(甚至没有IList)
IEnumerable.Any (扩展方法 – MSDN)
确定序列的任何元素是否满足条件。
这在.NET 3.5中是新的,并且使用Func(TSource,bool)作为参数,所以这是为了与lambdaexpression式和LINQ一起使用的。
在行为上,这些是相同的。
区别在于Any是System.Linq.Enumerable上定义的任何IEnumerable<T>
的扩展方法。 它可以在任何IEnumerable<T>
实例上使用。
存在似乎不是一个扩展方法。 我的猜测是coll是List<T>
的types。 如果是这样,Exists是一个与Any非常类似的实例方法。
总之 , 这些方法基本上是一样的。 一个比另一个更普遍。
- 任何也有一个重载,它不接受任何参数,只是查找枚举中的任何项目。
- 存在没有这样的过载。
TLDR; 性能明智Any
似乎都比较慢 (如果我已经正确地设置这两个值几乎同时进行评估)
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列表生成器:
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; }
拥有10Mlogging
“任何:00:00:00.3770377存在:00:00:00.2490249”
有5Mlogging
“任何时间:00:00:00.0940094 Exists:00:00:00.1420142”
有1Mlogging
“任何:00:00:00.0180018存在:00:00:00.0090009”
在500k的时候,(我也翻过了评估的顺序,看是否没有额外的操作与先运行相关)。
“Exists:00:00:00.0050005 Any:00:00:00.0100010”
有了10万条logging
“Exists:00:00:00.0010001 Any:00:00:00.0020002”
似乎Any
要慢2的数量级。
编辑:对于5和10Mlogging,我改变了它生成列表的方式, Exists
突然变得比Any
意思都慢,这意味着我testing的方式有问题。
新的testing机制:
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")) { } }
编辑2:好吧,以消除生成testing数据的任何影响我写这一切文件,现在从那里读取它。
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
“任何时间:00:00:00.1640164 Exists:00:00:00.0750075”
5M
“任何:00:00:00.0810081存在:00:00:00.0360036”
1M
“任何时间:00:00:00.0190019 Exists:00:00:00.0070007”
500K
“任何:00:00:00.0120012 Exists:00:00:00.0040004”
作为Matas关于基准testing的回答的延续。
TL / DR :Exists()和Any()同样快。
首先:使用秒表进行基准testing并不准确( 请参阅系列1的不同但类似的主题 ),但它比DateTime更精确。
获得真正精确的读数的方法是使用性能分析。 但是,了解两种方法的性能是如何相互影响的一种方法是通过执行两种方法的负载 ,然后比较每种方法的最快执行时间。 这样一来,JITing和其他噪音给我们带来的是不好的读数(这确实是无关紧要的),因为从某种意义上来说,这两次执行“ 同样是错误的”。
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; }
执行上述代码4次后(依次在1 000 000个元素的列表上执行1 000个Exists()
和Any()
),不难发现这些方法几乎同样快速。
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
有一些细微的差别,但是不能被背景噪音所解释。 我的猜测是,如果一个人可以做10 000或10万个Exists()
和Any()
,那么这个微小的差别就会多多lessless消失。
此外,只有当值是booltypes时才能使用。 通常这与谓词一起使用。 任何谓词都会被用来查找是否有任何元素满足给定的条件。 在这里,你只是从你的元素到布尔属性做一个地图。 它将searchValue属性为true的“i”。 一旦完成,该方法将返回true。
当你纠正测量 – 如上所述:任何和存在,并增加平均 – 我们会得到以下输出:
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.