前言 最近在讀一本電子書:LINQ Succinctly 內容從LINQ觀念到實作都有介紹,以新手來說一百頁左右的內容算很好上手,其中內容有一段敘述引起我的好奇
Using Any() instead of Count() != 0 usually conveys the intent of the code better, and in some circumstances, can perform better.
因為本人過往在撰寫程式時,習慣使用Count()來判斷集合中是否有物件,所以就來測試看看
List比較 我們先建立一個Product類別,然後使用For迴圈建立一個Product的List集合,之後使用StopWatch去測量已耗用時間 除了上述兩種情況,這邊額外新增測試 List 的 Count屬性 我們新增一個主控台應用程式,範例程式碼如下:
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 using System;using System.Collections.Generic;using System.Linq;using System.Diagnostics;namespace AnyCountTest { class Program { static void Main (string [] args ) { List<Product> products = new List<Product>(); for (int i = 0 ; i < 1000000 ; i++) { products.Add(new Product { Id = Guid.NewGuid(), Name = $"Product{i} " , No = i }); } Stopwatch sw = new Stopwatch(); int num = 0 ; Console.WriteLine($"------------------------------List集合開始測試" ); for (int i = 0 ; i < 5 ; i++) { sw.Restart(); foreach (var item in products) { if (products.Count != 0 ) { num++; } } sw.Stop(); num = 0 ; Console.WriteLine($"Count屬性:{sw.Elapsed} " ); sw.Restart(); foreach (var item in products) { if (products.Count() != 0 ) { num++; } } sw.Stop(); num = 0 ; Console.WriteLine($"Count() :{sw.Elapsed} " ); sw.Restart(); foreach (var item in products) { if (products.Any()) { num++; } } sw.Stop(); num = 0 ; Console.WriteLine($"Any :{sw.Elapsed} " ); Console.WriteLine($"第{i + 1 } 次測試完畢--------------------------" ); } Console.ReadLine(); } } class Product { public Guid Id { get ; set ; } public string Name { get ; set ; } public int No { get ; set ; } } }
讓我們來看看輸出結果:
Count>Count()>Any()
從上述的結果可以看到如果有 Count
屬性時,效率最佳所以一定優先使用,Count()
比Any()
好,因為如果來源的執行個體型別有實作ICollection<T>
,則會直接取用Count屬性,但這邊100萬筆資料來看,三者效能差異不會差得太過誇張,再來我們看一下Linq-to-Entities
Linq-to-Entities比較 首先在專案的 NuGet 安裝 EntityFramework 套件,之後我們先在DB準備好5000筆的商品資料 過程不是這篇重點所以略過,這邊自己可以選擇哪種做法比較方便,我是採用DB First方式,再寫迴圈把資料灌入資料表 準備好資料後開始測試,程式碼範例如下:
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 53 54 55 56 57 58 using System;using System.Collections.Generic;using System.Linq;using System.Diagnostics;namespace AnyCountTest { class Program { static void Main (string [] args ) { using (LinqTestEntities1 db = new LinqTestEntities1()) { var products = from i in db.Product select i; Stopwatch sw = new Stopwatch(); int num = 0 ; Console.WriteLine($"------------------------------Entity集合開始測試" ); for (int i = 0 ; i < 5 ; i++) { sw.Restart(); foreach (var item in products) { if (products.Count() != 0 ) { num++; } } sw.Stop(); num = 0 ; Console.WriteLine($"Count() :{sw.Elapsed} " ); sw.Restart(); foreach (var item in products) { if (products.Any()) { num++; } } sw.Stop(); num = 0 ; Console.WriteLine($"Any :{sw.Elapsed} " ); Console.WriteLine($"第{i + 1 } 次測試完畢--------------------------" ); } Console.ReadLine(); } } } }
結果:
Any()>Count()
這邊看到結果,因為第一次執行的時候會經過JIT編譯,這會造成第一次呼叫時比較花時間,所以第一次測試結果可以跳過不參考。總共資料有5000筆,可以看出效率上Count()
就比Any()
慢上許多,所以這邊就務必去使用Any()去作判斷
Linq-to-Entities比較(附加條件) Count()
和 Any()
各自都含有另一個多載方法
1 2 3 4 5 public static int Count <TSource > (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,bool > predicate ) ;public static bool Any <TSource > (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,bool > predicate ) ;
所以讓我們來改寫一下上面的程式碼,為兩個方法新增條件:
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 53 54 55 56 57 58 using System;using System.Collections.Generic;using System.Linq;using System.Diagnostics;namespace AnyCountTest { class Program { static void Main (string [] args ) { using (LinqTestEntities1 db = new LinqTestEntities1()) { var products = from i in db.Product select i; Stopwatch sw = new Stopwatch(); int num = 0 ; Console.WriteLine($"------------------------------Entity集合開始測試" ); for (int i = 0 ; i < 5 ; i++) { sw.Restart(); foreach (var item in products) { if (products.Count(x => x.No > 1000 ) != 0 ) { num++; } } sw.Stop(); num = 0 ; Console.WriteLine($"Count() :{sw.Elapsed} " ); sw.Restart(); foreach (var item in products) { if (products.Any(x => x.No > 1000 )) { num++; } } sw.Stop(); num = 0 ; Console.WriteLine($"Any :{sw.Elapsed} " ); Console.WriteLine($"第{i + 1 } 次測試完畢--------------------------" ); } Console.ReadLine(); } } } }
結果:
Any()>Count()
這邊可以看到,比起剛剛無條件的情況,加入條件時效能差異更大了,如果資料量變大,後果可不堪設想…所以這邊也務必使用Any()
另外當 Count()
遇到使用 yield return 時,也會出現效能問題,所以也應該盡量避免使用,詳細原因可以參考下面小朱大的文章
這邊提醒一下條件使用 Count() != 0 或 Count() > 0皆不影響上述的測試結果
結語 如果能用 Count 屬性時,一定要用,若是 Linq-to-Entities,沒有 Count 屬性可用,請務必優先使用 Any() 方法,在參考資料有更多詳細的解說,可以多參考,這篇文章用簡單的範例去做效能測試驗證,希望能用淺顯易懂的方式讓大家了解囉
參考資料 小朱® 的技術隨手寫 - Any() vs. Count() 何時可用? 何時不可用?
Which method performs better: .Any() vs .Count() > 0?
query result what should i use Count() or Any()
Count property vs Count() method?