[.NET][LINQ] Any() vs. Count() 何時可用? 何時不可用?

LINQ 語法中有兩個很令人玩味的方法,一個是 Any(),另一個則是 Count(),Any() 的功能是判斷集合中是否有物件,Count() 則是用來計算集合中的物件數量,功能其實很像,以一般的使用習慣來說,我們多半會使用 Count() 來判斷集合中是否有物件,而且在大多數的情況下是沒問題的。

LINQ 語法中有兩個很令人玩味的方法,一個是 Any(),另一個則是 Count(),Any() 的功能是判斷集合中是否有物件,Count() 則是用來計算集合中的物件數量,功能其實很像,以一般的使用習慣來說,我們多半會使用 Count() 來判斷集合中是否有物件,而且在大多數的情況下是沒問題的。

不過因為 Any() 和 Count() 有一個本質上的差異:Any() 使用 IEnumerator.GetEnumerator() 和 MoveNext() 來判斷是否有物件,而 Count() 會巡覽整個集合後回傳個數,因此,當遇到使用 yield return 回傳集合的方法時,若使用了 Count() 的話,當 yield return 回傳的物件愈多,表示 Count() 要巡覽的集合愈大,速度也會愈慢。(Reference: http://blog.donnfelker.com/2009/06/22/linq-any-vs-count/)

上面的說法基本上在 LINQ to SQL, LINQ to Entities 或對外部資料來源進行 LINQ 時是有效的,我們以 LINQ to SQL 為例:

-- Generated by Count()
SELECT COUNT(*) AS [value]
FROM [dbo].[Registrations] AS [t0]
WHERE [t0].[EmailId] = @p0 and [t0].Password = @p1

-- Generated by Any()
SELECT 
    (CASE 
        WHEN EXISTS(
            SELECT NULL AS [EMPTY]
            FROM [dbo].[Registrations] AS [t0]
            WHERE [t0].[EmailId] = @p0 and [t0].Password = @p1
            ) THEN 1
        ELSE 0
     END) AS [value]

(Source: http://stackoverflow.com/questions/2856965/query-result-what-should-i-use-count-or-any)

以 T-SQL 的觀點來說,Any() 的指令對 DBMS 來說因為不需要去讀取索引與資料分頁,所以速度上會比 SELECT COUNT( * ) 要快,對於其他類似的 LINQ Provider 來說,多半也會有相同的作法。

不過以上的討論僅限於對外部來源的 Any()/Count() 的差異,如果今天我們的資料來源是 .NET Framework 內的集合物件,例如陣列,ICollection 介面實作的物件 (ex: List<T>, Dictionary<TKey, TValue>),那麼在 LINQ 的 Any() 和 Count() 實作中,都會判斷來源的 IEnumerable<T> 是否是 IList<T> 的實作,如果是的話,會自動取用 IList<T>.Count 屬性值,而不會使用上面討論的 IEnumerator.GetEnumerator(),這時使用 Any() / Count() 的效能差異會變得極小,幾乎會相同。

因此,我們可以推論出下列結果:

  • 當集合是來自不確定個數的 IEnumerable<T> 時,請使用 Any() 來判斷集合中是否有資料。
  • 當集合是來自外部資料來源 (ex: LINQ to SQL, LINQ to Entities) 時,請使用 Any()
  • 當集合是來自 .NET Framework 內建的集合時,不論 Any() 或 Count() 都可以

 

Reference: