在集合上使用Linq,如下代碼行之間有什麼區別? dom
if(!coll.Any(i => i.Value))
和 ide
if(!coll.Exists(i => i.Value))
更新1 性能
當我反彙編.Exists
看起來沒有代碼。 測試
更新2 spa
任何人都知道爲何這個沒有代碼? pwa
TLDR; 性能方面Any
看起來都比較慢 (若是我已正確設置它以幾乎同時評估這兩個值) code
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")) { }
測試列表生成器: orm
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; }
擁有10M記錄 ip
「任何:00:00:00.3770377存在:00:00:00.2490249」 get
有5M記錄
「任何:00:00:00.0940094存在:00:00:00.1420142」
擁有1M記錄
「任何:00:00:00.0180018存在:00:00:00.0090009」
使用500k,(我也按順序翻轉它們進行評估,以查看是否沒有與先運行的任何操做相關聯的額外操做。)
「存在:00:00:00.0050005任何:00:00:00.0100010」
擁有100k記錄
「存在:00:00:00.0010001任何:00:00:00.0020002」
彷佛Any
比較慢的2級。
編輯:對於5和10M記錄,我改變了它生成列表的方式,而且Exists
忽然變得慢於Any
,這意味着我測試的方式有問題。
新的測試機制:
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:好的,爲了消除生成測試數據的任何影響我把它所有寫到文件中,如今從那裏讀取它。
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存在:00:00:00.0750075」
5M
「任何:00:00:00.0810081存在:00:00:00.0360036」
1M
「任何:00:00:00.0190019存在:00:00:00.0070007」
500K
「任何:00:00:00.0120012存在:00:00:00.0040004」
TL / DR :Exists()和Any()一樣快。
首先:使用秒錶進行基準測試並不精確( 請參閱series0ne關於不一樣但相似的主題的答案 ),但它比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()
,那麼這個微小的差別會或多或少地消失。
當您更正測量值時 - 如上所述:任意和存在,並添加平均值 - 咱們將得到如下輸出:
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.
區別在於Any是System.Linq.Enumerable上定義的任何IEnumerable<T>
的擴展方法。 它能夠在任何IEnumerable<T>
實例上使用。
存在彷佛不是一種擴展方法。 個人猜想是coll的類型爲List<T>
。 若是是這樣Exists是一個實例方法,其功能與Any很是類似。
簡而言之 , 這些方法基本相同。 一個比另外一個更廣泛。
此外,這僅在Value爲bool類型時纔有效。 一般,這與謂詞一塊兒使用。 一般使用任何謂詞來查找是否存在知足給定條件的任何元素。 在這裏,您只須要從元素i到bool屬性進行映射。 它將搜索Value屬性爲true的「i」。 完成後,該方法將返回true。