Phương thức nào thực hiện tốt hơn: .Any () so với .Count ()> 0?


578

trong System.Linqkhông gian tên, giờ đây chúng ta có thể mở rộng các IEnumerable của mình để có các phương thức mở rộng Any ()Count () .

Gần đây tôi đã nói rằng nếu tôi muốn kiểm tra xem một bộ sưu tập có chứa 1 hoặc nhiều mục bên trong nó hay không, tôi nên sử dụng .Any()phương thức mở rộng thay vì .Count() > 0phương thức mở rộng vì .Count()phương thức mở rộng phải lặp qua tất cả các mục.

Thứ hai, một số bộ sưu tập có một thuộc tính (không phải là một phương thức mở rộng) là Counthoặc Length. Nó sẽ tốt hơn để sử dụng những người, thay vì .Any()hoặc .Count()?

phải / nae?


Tốt hơn để sử dụng Bất kỳ () trên Số lượng và Đếm trên Bộ sưu tập. Nếu ai đó cảm thấy viết '(somecollection.Count> 0)' sẽ gây nhầm lẫn hoặc gây ra các vấn đề về khả năng đọc, tốt hơn nên viết nó như một phương thức mở rộng có tên là Any (). Rồi mọi người hài lòng. Hiệu suất-khôn ngoan cũng như dễ đọc-khôn ngoan. Vì vậy, tất cả mã của bạn sẽ có tính nhất quán và nhà phát triển riêng lẻ trong dự án của bạn không cần phải lo lắng về việc chọn Count vs Any.
Mahesh Bongani

Câu trả lời:


709

Nếu bạn đang bắt đầu với một cái gì đó mà có một .Lengthhoặc .Count(ví dụ như ICollection<T>, IList<T>, List<T>, vv) - thì đây sẽ là lựa chọn nhanh nhất, vì nó không cần phải đi qua GetEnumerator()/ MoveNext()/ Dispose()chuỗi theo yêu cầu của Any()kiểm tra cho một tổ chức phi rỗng IEnumerable<T>chuỗi .

Đối với chỉ IEnumerable<T>, sau đó Any()sẽ thường được nhanh hơn, vì nó chỉ có nhìn vào một lần lặp. Tuy nhiên, lưu ý rằng việc triển khai LINQ-to-Object Count()không kiểm tra ICollection<T>(sử dụng .Countnhư một tối ưu hóa) - vì vậy nếu nguồn dữ liệu cơ bản của bạn trực tiếp là một danh sách / bộ sưu tập, sẽ không có sự khác biệt lớn. Đừng hỏi tôi tại sao nó không sử dụng ICollection...

Tất nhiên, nếu bạn đã sử dụng LINQ để lọc nó, v.v. Where, bạn sẽ có một chuỗi dựa trên khối lặp, và vì vậy việc ICollection<T>tối ưu hóa này là vô ích.

Nói chung với IEnumerable<T>: gắn bó với Any();-p


9
Marc: ICollection <T> không thực sự xuất phát từ ICollection. Tôi cũng ngạc nhiên, nhưng Reflector không nói dối.
Bryan Watts

7
Không có bất kỳ () nào thực hiện kiểm tra giao diện ICollection và kiểm tra sau cho thuộc tính Count?
derigel

313
Tôi nghĩ rằng có một lý do khác để sử dụng Any () hầu hết thời gian. Nó báo hiệu ý định chính xác của nhà phát triển. Nếu bạn không quan tâm đến việc biết số lượng vật phẩm, nhưng chỉ khi có một số vật phẩm, thì somecollection.Any () đơn giản và rõ ràng hơn somecollection.Count> 0
TJKjaer

13
@huttelihut - Có bao nhiêu nhà phát triển bạn biết ai thực sự bối rối trước tuyên bố này (somecollection.Count > 0)? Có phải tất cả các mã của chúng tôi trước khi giới thiệu phương thức .Any () của LINQ đều khó hiểu?
CraigTP

25
@JLRishe - Tôi vẫn cảm thấy someCollection.Count > 0rõ ràng someCollection.Any()và có thêm lợi ích của hiệu suất cao hơn và không yêu cầu LINQ. Cấp, đây là một trường hợp rất đơn giản và các cấu trúc khác sử dụng toán tử LINQ sẽ truyền đạt ý định của nhà phát triển rõ ràng hơn nhiều so với tùy chọn không phải LINQ tương đương.
CraigTP

65

Lưu ý: Tôi đã viết câu trả lời này khi Entity Framework 4 là thực tế. Điểm của câu trả lời này là không tham gia vào thử nghiệm tầm thường .Any()so với .Count()hiệu suất. Vấn đề là báo hiệu rằng EF không hoàn hảo. Các phiên bản mới tốt hơn ... nhưng nếu bạn có một phần mã chậm và sử dụng EF, hãy kiểm tra với TSQL trực tiếp và so sánh hiệu suất thay vì dựa vào các giả định (đó .Any()là LUÔN nhanh hơn .Count() > 0).


Mặc dù tôi đồng ý với hầu hết các câu trả lời và bình luận được bình chọn - đặc biệt là về ý định của nhà phát triểnAny tín hiệu tốt hơn - tôi đã gặp tình huống Count nhanh hơn theo thứ tự cường độ trên SQL Server (EntityFramework 4).Count() > 0

Đây là truy vấn với Anyngoại lệ hết thời gian chờ (trên ~ 200.000 bản ghi):

con = db.Contacts.
    Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
        && !a.NewsletterLogs.Any(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr)
    ).OrderBy(a => a.ContactId).
    Skip(position - 1).
    Take(1).FirstOrDefault();

Count phiên bản được thực hiện trong vấn đề mili giây:

con = db.Contacts.
    Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
        && a.NewsletterLogs.Count(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr) == 0
    ).OrderBy(a => a.ContactId).
    Skip(position - 1).
    Take(1).FirstOrDefault();

Tôi cần tìm một cách để xem SQL chính xác mà cả hai LINQ tạo ra - nhưng rõ ràng có một sự khác biệt lớn về hiệu năng giữa CountAnytrong một số trường hợp, và thật không may là dường như bạn không thể gắn bó Anytrong mọi trường hợp.

EDIT: Đây là các SQL được tạo. Người đẹp như bạn có thể thấy;)

ANY:

thực hiện sp_executesql N'SELECT TOP (1) 
[Project2]. [ContactId] NHƯ [ContactId], 
[Project2]. [CompanyId] NHƯ [CompanyId], 
[Project2]. [Tên liên hệ] NHƯ [Tên liên hệ], 
[Project2]. [Tên đầy đủ] NHƯ [Tên đầy đủ], 
[Project2]. [ContactStatusId] NHƯ [ContactStatusId], 
[Project2]. [Tạo] NHƯ [Đã tạo]
TỪ (CHỌN [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Tạo] AS [Tạo], row_number () QUÁ (ĐẶT HÀNG B [NG [Project2]. [ContactId] ASC) AS [row_number]
    TỪ (CHỌN 
        [Extent1]. [ContactId] NHƯ [ContactId], 
        [Extent1]. [CompanyId] NHƯ [CompanyId], 
        [Extent1]. [Tên liên hệ] NHƯ [Tên liên hệ], 
        [Extent1]. [FullName] NHƯ [FullName], 
        [Extent1]. [ContactStatusId] NHƯ [ContactStatusId], 
        [Extent1]. [Tạo] NHƯ [Đã tạo]
        TỪ [dbo]. [Liên hệ] NHƯ [Extent1]
        WHERE ([Extent1]. [CompanyId] = @ p__linq__0) VÀ ([Extent1]. [ContactStatusId] <= 3) VÀ (KHÔNG EXISTS (CHỌN 
            1 NHƯ [C1]
            TỪ [dbo]. [NewsletterLog] NHƯ [Extent2]
            WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) VÀ (6 = [Extent2]. [NewsletterLogTypeId])
        ))
    ) NHƯ [Dự án2]
) NHƯ [Dự án2]
Ở ĐÂU [Project2]. [Row_number]> 99
ĐẶT HÀNG B [NG [Project2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

COUNT:

thực hiện sp_executesql N'SELECT TOP (1) 
[Project2]. [ContactId] NHƯ [ContactId], 
[Project2]. [CompanyId] NHƯ [CompanyId], 
[Project2]. [Tên liên hệ] NHƯ [Tên liên hệ], 
[Project2]. [Tên đầy đủ] NHƯ [Tên đầy đủ], 
[Project2]. [ContactStatusId] NHƯ [ContactStatusId], 
[Project2]. [Tạo] NHƯ [Đã tạo]
TỪ (CHỌN [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Tạo] AS [Tạo], row_number () QUÁ (ĐẶT HÀNG B [NG [Project2]. [ContactId] ASC) AS [row_number]
    TỪ (CHỌN 
        [Project1]. [ContactId] NHƯ [ContactId], 
        [Project1]. [CompanyId] NHƯ [CompanyId], 
        [Project1]. [Tên liên hệ] NHƯ [Tên liên hệ], 
        [Project1]. [Tên đầy đủ] NHƯ [Tên đầy đủ], 
        [Project1]. [ContactStatusId] NHƯ [ContactStatusId], 
        [Project1]. [Tạo] NHƯ [Đã tạo]
        TỪ (CHỌN 
            [Extent1]. [ContactId] NHƯ [ContactId], 
            [Extent1]. [CompanyId] NHƯ [CompanyId], 
            [Extent1]. [Tên liên hệ] NHƯ [Tên liên hệ], 
            [Extent1]. [FullName] NHƯ [FullName], 
            [Extent1]. [ContactStatusId] NHƯ [ContactStatusId], 
            [Extent1]. [Đã tạo] NHƯ [Đã tạo], 
            (LỰA CHỌN 
                QUẬN (1) NHƯ [A1]
                TỪ [dbo]. [NewsletterLog] NHƯ [Extent2]
                WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) VÀ (6 = [Extent2]. [NewsletterLogTypeId])) NHƯ [C1]
            TỪ [dbo]. [Liên hệ] NHƯ [Extent1]
        ) NHƯ [Dự án1]
        Ở ĐÂU ([Project1]. [CompanyId] = @ p__linq__0) VÀ ([Project1]. [ContactStatusId] <= 3) VÀ (0 = [Project1]. [C1])
    ) NHƯ [Dự án2]
) NHƯ [Dự án2]
Ở ĐÂU [Project2]. [Row_number]> 99
ĐẶT HÀNG B [NG [Project2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

Có vẻ như Where with EXISTS hoạt động kém hơn nhiều so với tính toán Count và sau đó thực hiện Where with Count == 0.

Hãy cho tôi biết nếu các bạn thấy một số lỗi trong phát hiện của tôi. Điều có thể được loại bỏ khỏi tất cả những điều này bất kể thảo luận Any vs Count là bất kỳ LINQ nào phức tạp hơn sẽ tốt hơn khi được viết lại dưới dạng Thủ tục lưu trữ;).


2
Rất thích xem một số gói Truy vấn Sql được tạo bởi mỗi truy vấn linq cho từng kịch bản.
Pure.Krom

43
dựa trên SQL, tất cả những gì tôi có thể nói là: cả hai truy vấn đều trông thật kinh khủng. Tôi biết có một lý do tôi thường viết TSQL của riêng mình ...
Marc Gravell

! Bất kỳ ai cũng sẽ phải xem qua tất cả các hàng giống như Count. Rằng ví dụ của bạn cho kết quả khủng khiếp như vậy là hơi lạ, trong trường hợp xấu nhất! Bất kỳ chỉ nên chậm hơn một chút so với Count. Trong trường hợp của bạn, tôi sẽ tìm cách để đơn giản hóa việc lựa chọn, có thể chia nó ra theo từng giai đoạn hoặc sắp xếp lại các điều kiện nếu điều đó là có thể. Nhưng quan điểm của bạn là quy tắc Any tốt hơn so với quy tắc Count không tuân thủ! Bất kỳ điều gì tốt hơn Count là một điều rất tốt.
Bent

25

Vì đây là một chủ đề khá phổ biến và câu trả lời khác nhau, tôi đã có một cái nhìn mới về vấn đề này.

Kiểm tra env: EF 6.1.3, SQL Server, bản ghi 300k

Mô hình bảng :

class TestTable
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public string Surname { get; set; }
}

Mã kiểm tra:

class Program
{
    static void Main()
    {
        using (var context = new TestContext())
        {
            context.Database.Log = Console.WriteLine;

            context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);

            Console.ReadLine();
        }
    }
}

Các kết quả:

Bất kỳ () ~ 3ms

Đếm () ~ 230ms cho truy vấn đầu tiên, ~ 400ms cho truy vấn thứ hai

Nhận xét:

Đối với trường hợp của tôi, EF đã không tạo SQL như @Ben được đề cập trong bài đăng của anh ấy.


4
Để so sánh đúng, bạn nên làm Count() > 0. : D
Andrew

1
Andrew, Count ()> 0 sẽ không chạy khác với Count () trong thử nghiệm cụ thể này.
CodeMonkeyForHire

11

BIÊN TẬP: nó đã được sửa trong phiên bản EF 6.1.1. và câu trả lời này không còn thực tế nữa

Đối với SQL Server và EF4-6, Count () thực hiện nhanh hơn khoảng hai lần so với Any ().

Khi bạn chạy Table.Any (), nó sẽ tạo ra một cái gì đó như ( cảnh báo: đừng làm tổn thương não khi cố gắng hiểu nó )

SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [Table] AS [Extent1]
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [Table] AS [Extent2]
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]

yêu cầu 2 lần quét các hàng với điều kiện của bạn.

Tôi không thích viết Count() > 0vì nó che giấu ý định của tôi. Tôi thích sử dụng vị ngữ tùy chỉnh cho việc này:

public static class QueryExtensions
{
    public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
    {
        return source.Count(predicate) > 0;
    }
}

Tôi cũng nhận thấy điều này. SQL Any () hoàn toàn không có ý nghĩa gì cả. Tôi không chắc tại sao họ không làm: CASE KHI (EXISTS (sql)) THÌ 1 ELSE 0 END. Tôi không thể nghĩ ra lý do tại sao họ cần phải KHÔNG
TRAO ĐỔI

Điều này là sai. Bạn tìm thấy một kế hoạch truy vấn xấu bởi cơ hội ngẫu nhiên. Điều này xảy ra. Bất kỳ là, hầu như luôn luôn, nhanh hơn.
usr

Tôi đã kiểm tra sql được tạo trong 6.1.3, họ đã sửa nó: CHỌN TRƯỜNG HỢP KHI (EXISTS (CHỌN 1 NHƯ [C1] TỪ [dbo]. [TestTables] AS [Extent1] WHERE [Extent1]. [Id]> 1000)) THEN cast (1 as bit) ELSE cast (0 as bit) END AS [C1] TỪ (CHỌN 1 AS X) AS [SingleRowTable1]
Ben

6

Nó phụ thuộc, tập dữ liệu lớn như thế nào và yêu cầu hiệu suất của bạn là gì?

Nếu không có gì khổng lồ thì hãy sử dụng dạng dễ đọc nhất, đối với bản thân tôi là bất kỳ, vì nó ngắn hơn và dễ đọc hơn là một phương trình.


2

Về phương thức Count () , nếu IEnumarableICollection , thì chúng ta không thể lặp lại tất cả các mục vì chúng ta có thể truy xuất trường Count của ICollection , nếu IEnumerable không phải là ICollection, chúng ta phải lặp đi lặp lại trên tất cả các mục trong một thời gian với một MoveNext , hãy xem Mã .NET Framework:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) 
        throw Error.ArgumentNull("source");

    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) 
        return collectionoft.Count;

    ICollection collection = source as ICollection;
    if (collection != null) 
        return collection.Count;

    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        checked
        {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}

Tham khảo: Nguồn tham khảo


2

Bạn có thể thực hiện một thử nghiệm đơn giản để tìm ra điều này:

var query = //make any query here
var timeCount = new Stopwatch();
timeCount.Start();
if (query.Count > 0)
{
}
timeCount.Stop();
var testCount = timeCount.Elapsed;

var timeAny = new Stopwatch();
timeAny.Start();
if (query.Any())
{
}
timeAny.Stop();
var testAny = timeAny.Elapsed;

Kiểm tra các giá trị của testCount và testAny.


1
Dưới đây là bài kiểm tra với mã của bạn cho thuộc tính Count vs Any () Count property thắng vs Any () với + 2x - link
Stanislav Prusac

1
Để có kết quả tốt hơn, bạn có thể thực hiện các so sánh này 1000 lần (hoặc hơn). Nó giúp trung bình ra kết quả và tránh bất kỳ đột biến ngẫu nhiên.
La Mã

Khi bạn đang thử nghiệm như phương pháp đã đề cập ở trên, bạn cần xem xét nhiều yếu tố hơn, chẳng hạn như tải trên cơ sở dữ liệu / mạng của bạn, lên kế hoạch lưu trữ trong phía cơ sở dữ liệu, v.v. Vì vậy, để kiểm tra chính xác, bạn cũng nên thiết kế một môi trường chính xác và tách biệt.
Vahid Farahmandian

để so sánh tốt hơn nên được Countthay thế bằng phương thức Count () so với .Any () không phải là thuộc tính. Bạn cần thời gian lặp lại.
daremachine

0

Nếu bạn đang sử dụng Entity Framework và có một bảng khổng lồ với nhiều bản ghi thì Any () sẽ nhanh hơn nhiều. Tôi nhớ một lần tôi muốn kiểm tra xem một cái bàn có trống không và nó có hàng triệu hàng. Phải mất 20-30 giây để Count ()> 0 hoàn thành. Đó là ngay lập tức với Any () .

Bất kỳ () nào cũng có thể là một cải tiến hiệu suất bởi vì nó có thể không phải lặp lại bộ sưu tập để có được số lượng thứ. Nó chỉ cần phải đánh một trong số họ. Hoặc, giả sử, LINQ-to-Entities, SQL được tạo sẽ là NẾU EXISTS (...) thay vì CHỌN COUNT ... hoặc thậm chí CHỌN * ....

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.