Hangi yöntem daha iyi performans gösterir: .Any () vs .Count ()> 0?


578

içinde System.Linqad, şimdi bizim uzatabilirsiniz IEnumerable var olması Herhangi () ve () Sayım uzatma yöntemleri .

Son zamanlarda, bir koleksiyonun içinde 1 veya daha fazla öğe olup olmadığını kontrol etmek istersem , uzatma yöntemi tüm öğeler arasında yineleme yapmak zorunda olduğum için, .Any()uzantı yöntemi yerine uzantı yöntemini kullanmam gerektiğini söyledim ..Count() > 0.Count()

İkinci olarak, bazı koleksiyonları sahip özelliği olan (bir uzatma yöntemi) Countya da Length. Bunları kullanmak .Any()veya yerine daha iyi olur .Count()mu?

evet / nae?


Numaralandırılabilirlerde Any () kullanımı ve Koleksiyonlara Güven. Birisi '(somecollection.Count> 0)' yazmayı hissederse, okunabilirlikle ilgili sorunlara neden olur veya karışırsa, bunu bir uzantı yöntemi olarak Any () olarak adlandırmanız daha iyi olur. Sonra herkes tatmin oldu. Performans açısından ve Okunabilirlik açısından. Böylece, tüm kodlarınız tutarlı olacak ve projenizdeki bireysel geliştiricilerin Count vs Any'u seçmek konusunda endişelenmesine gerek yok.
Mahesh Bongani

Yanıtlar:


709

Eğer bir olan bir şey ile başlayan edin .Lengthveya .Count(gibi ICollection<T>, IList<T>, List<T>, vs) - bu geçmesi gerekmez çünkü o zaman bu, en hızlı seçenek olacaktır GetEnumerator()/ MoveNext()/ Dispose()gerektirdiği dizisi Any()boş olmayan kontrol etmek IEnumerable<T>dizisi .

Sadece için IEnumerable<T>, o Any()olacak genellikle sadece bir yineleme bakmak olduğu gibi, çabuk ol. Bununla birlikte, LINQ-Objects uygulamasının Count()kontrol ettiğini ICollection<T>( .Countoptimizasyon olarak kullanarak ) unutmayın; bu nedenle, temel veri kaynağınız doğrudan bir liste / koleksiyonsa, büyük bir fark olmaz. Bana neden jenerik olmayan kullanmadığını sorma ICollection...

Tabii ki, vb. ( WhereVb.) Filtrelemek için LINQ kullandıysanız , yineleyici blok tabanlı bir diziye sahip olacaksınız ve bu nedenle bu ICollection<T>optimizasyon işe yaramaz.

Genel olarak IEnumerable<T>: Any();-p ile sopa


9
Marc: ICollection <T> aslında ICollection'dan kaynaklanmıyor. Ben de şaşırdım ama Reflektör yalan söylemiyor.
Bryan Watts

7
Any () uygulaması ICollection arabirimini ve sonra Count özelliğini kontrol etmez mi?
derigel

313
Çoğu zaman Any () kullanmanın başka bir nedeni olduğunu düşünüyorum. Geliştiricinin kesin amacını gösterir. Öğelerin sayısını bilmekle ilgilenmiyorsanız, ancak sadece bazıları varsa, bazı toplama.Any () bazı toplamadan daha basit ve daha açıktır. Sayı> 0
TJKjaer

13
@huttelihut - İfade ile kimin kafası karıştığını kaç geliştirici tanıyorsunuz (somecollection.Count > 0)? LINQ'nun .Any () yönteminin kullanılmasından önce tüm kodlarımızı anlamak zor muydu?
CraigTP

25
@JLRishe - Hâlâ daha someCollection.Count > 0net someCollection.Any()ve daha yüksek performans ve LINQ gerektirmeme avantajına sahip olduğunu hissediyorum . Bu çok basit bir durum ve LINQ operatörlerini kullanan diğer yapılar geliştiricilerin niyetini LINQ olmayan seçeneğe göre çok daha net bir şekilde iletecek.
CraigTP

65

Not: Bu cevabı Entity Framework 4 gerçek olduğunda yazdım. Bu cevabın amacı önemsizliğe .Any()karşı .Count()performans testine girmek değildi . Mesele, EF'in mükemmel olmaktan uzak olduğunu göstermekti. Daha yeni sürümler daha iyidir ... ancak kodun yavaş olan bir parçası varsa ve EF kullanıyorsa, doğrudan TSQL ile test edin ve varsayımlara dayanmak yerine performansı karşılaştırın ( .Any()her zamankinden daha hızlıdır .Count() > 0).


Ben kabul ederken en cevabı ve yorum yukarı yüksek oyu - özellikle noktası üzerinde Anysinyaller niyet geliştirici daha iyi Count() > 0- Kont SQL Server (EntityFramework 4) üzerine büyüklük sırasına göre daha hızlı olduğu durum yaşadım.

İşte Anythew zaman aşımı istisnası (~ 200.000 kayıtlarda) ile ilgili sorgu :

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 milisaniye içinde çalıştırılan sürüm:

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();

Ben tam bir SQL hem LINQs üretmek ne olduğunu görmek için bir yol bulmalıyız - ama arasında büyük performans fark yoktur açıktır Countve Anybazı durumlarda, ve ne yazık ki sadece sopa olamaz görünüyor Anyher durumda.

EDIT: İşte üretilen SQL'ler. Gördüğünüz gibi güzellikler;)

ANY:

exec sp_executesql N'SELECT ÜST (1) 
[Proje2]. [ContactId] AS [ContactId], 
[Proje2]. [Şirket Kimliği] AS [Şirket Kimliği], 
[Proje2]. [KişiAdı] AS [KişiAdı], 
[Proje2]. [TamAd] AS [TamAd], 
[Proje2]. [ContactStatusId] AS [ContactStatusId], 
[Proje2]. [Oluşturuldu] AS [Oluşturuldu]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [KişiAdı], [Project2]. [TamAd] AS [FullName] [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Oluşturuldu] AS [Oluşturuldu], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
    BAŞLANGIÇ (SEÇ 
        [Extent1]. [ContactId] AS [ContactId], 
        [Extent1]. [CompanyId] AS [CompanyId], 
        [Extent1]. [KişiAdı] AS [KişiAdı], 
        [Tam1]. [TamAd] AS [TamAd], 
        [Extent1]. [ContactStatusId] AS [ContactStatusId], 
        [Extent1]. [Oluşturuldu] AS [Oluşturuldu]
        [Dbo] 'dan. [İletişim] AS [Extent1]
        NEREDE ([Extent1]. [CompanyId] = @ p__linq__0) AND ([Extent1]. [ContactStatusId] <= 3) VE (VAR DEĞİL (SEÇ 
            1 AS [C1]
            [Dbo] 'dan. [NewsletterLog] AS [Extent2]
            NEREDE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])
        ))
    ) AS [Proje2]
) AS [Proje2]
NEREDE [Proje2]. [Satır_sayısı]> 99
ORDER BY [Proje2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

COUNT:

exec sp_executesql N'SELECT ÜST (1) 
[Proje2]. [ContactId] AS [ContactId], 
[Proje2]. [Şirket Kimliği] AS [Şirket Kimliği], 
[Proje2]. [KişiAdı] AS [KişiAdı], 
[Proje2]. [TamAd] AS [TamAd], 
[Proje2]. [ContactStatusId] AS [ContactStatusId], 
[Proje2]. [Oluşturuldu] AS [Oluşturuldu]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [KişiAdı], [Project2]. [TamAd] AS [FullName] [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Oluşturuldu] AS [Oluşturuldu], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
    BAŞLANGIÇ (SEÇ 
        [Proje1]. [ContactId] AS [ContactId], 
        [Proje1]. [Şirket Kimliği] AS [Şirket Kimliği], 
        [Proje1]. [KişiAdı] AS [KişiAdı], 
        [Proje1]. [TamAd] AS [TamAd], 
        [Proje1]. [ContactStatusId] AS [ContactStatusId], 
        [Proje1]. [Oluşturuldu] AS [Oluşturuldu]
        BAŞLANGIÇ (SEÇ 
            [Extent1]. [ContactId] AS [ContactId], 
            [Extent1]. [CompanyId] AS [CompanyId], 
            [Extent1]. [KişiAdı] AS [KişiAdı], 
            [Tam1]. [TamAd] AS [TamAd], 
            [Extent1]. [ContactStatusId] AS [ContactStatusId], 
            [Oluşturuldu] AS [Oluşturuldu], 
            (SEÇ 
                COUNT (1) OLARAK [A1]
                [Dbo] 'dan. [NewsletterLog] AS [Extent2]
                NEREDE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) AND (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
            [Dbo] 'dan. [İletişim] AS [Extent1]
        ) AS [Proje1]
        NEREDE ([Project1]. [CompanyId] = @ p__linq__0) AND ([Project1]. [ContactStatusId] <= 3) AND (0 = [Project1]. [C1])
    ) AS [Proje2]
) AS [Proje2]
NEREDE [Proje2]. [Satır_sayısı]> 99
ORDER BY [Proje2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

EXISTS ile saf Nerede Count hesaplamak ve daha sonra Nerede Count == 0 ile yapmaktan daha kötü çalışır gibi görünüyor.

Bulgularımda bir hata görürseniz bana bildirin. Any vs Count tartışmasına bakılmaksızın tüm bunlardan çıkarılabilecek şey, daha karmaşık LINQ'nun Saklı Yordam olarak yeniden yazıldığında çok daha iyi olmasıdır;).


2
Her senaryo için her linq sorgusu tarafından oluşturulan bazı Sql Sorgu planlarını görmek isterim.
Pure.Krome

43
SQL dayanarak, söyleyebileceğim tek şey: her iki sorgu korkunç görünüyor. Normalde kendi TSQL'imi yazmamın bir nedeni olduğunu biliyordum ...
Marc Gravell

Herhangi biri Kont'un yaptığı gibi tüm satırlara bakmak zorundadır. Örneğinizin bu kadar korkunç bir sonuç vermesi, en kötü durumda biraz gariptir! Herhangi biri Kont'tan biraz daha yavaş olmalıdır. Sizin durumunuzda, seçimi basitleştirmek, belki de aşamalara ayırmak veya mümkünse koşulları yeniden sıralamak için yollar ararım. Ama herhangi biri Konttan daha iyidir kuralı tutmaz! Herhangi biri Kont'tan daha iyidir çok iyi bir kuraldır.
Bent

25

Bu oldukça popüler bir konu ve cevaplar farklı olduğu için soruna yeni bir bakış atmak zorunda kaldım.

Test env: EF 6.1.3, SQL Server, 300k kayıt

Masa modeli :

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

    public string Name { get; set; }

    public string Surname { get; set; }
}

Test kodu:

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();
        }
    }
}

Sonuçlar:

Herhangi bir () ~ 3 ms

Say () ilk sorgu için ~ 230ms, ikinci için ~ 400ms

Uyarılar:

Benim durumumda EF, yazısında bahsedilen @Ben gibi SQL üretmedi.


4
Doğru bir karşılaştırma için yapmanız gerekir Count() > 0. : D
Andrew

1
Andrew, Count ()> 0 bu testte Count () 'dan farklı çalışmaz.
CodeMonkeyForHire

11

EDIT: EF 6.1.1 sürümünde düzeltildi. ve bu cevap artık gerçek değil

SQL Server ve EF4-6 için Count (), Any () 'den yaklaşık iki kat daha hızlı performans gösterir.

Eğer Table.Any () çalıştırdığınızda, gibi bir şey üretecektir ( alert: anlamaya çalışırken beyin incitme )

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]

durumunuzla birlikte 2 satır tarama gerektirir.

Yazmayı sevmiyorum Count() > 0çünkü niyetimi gizliyor. Bunun için özel yüklem kullanmayı tercih ediyorum:

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

Bunu ben de fark ettim. Any () SQL hiç bir anlam ifade etmiyor. Neden yapmadıklarından emin değilim: VAR NE OLDUĞUNDA (VAR (sql)) SONRA 1 BAŞKA 0 SON. 0 dönmek için bir
VAR OLMAMALARI

Bu yanlış. Rastgele bir şansla kötü bir sorgu planı buldunuz. Bu olur. Herhangi biri, neredeyse her zaman daha hızlıdır.
usr

6.1.3'te oluşturulan sql'i kontrol ettim, düzelttiler: OLDUĞUNDA SEÇ (VAR (SEÇ 1 ([dbo] 'dan [C1] OLARAK SEÇ. [TestTables]] [Extent1] NEREDE [Extent1]. [Id]> 1000)) SONRA cast (bit 1 olarak) ELSE cast (bit 0 olarak) [C1R] OLARAK SONLANDIR (SEÇ 1 AS X) AS [SingleRowTable1]
Ben

6

Veri kümesinin büyüklüğü ve performans gereksinimleriniz nelerdir?

Devasa bir şey değilse, kendim için olan en okunabilir formu kullanın, çünkü bir denklem yerine daha kısa ve okunabilir.


2

Hakkında Kont () eğer yöntemle, IEnumarable bir olan ıcollection , o zaman olamaz yinelerler tüm öğelerinde biz alabilir çünkü Kont alanını ICollection eğer, IEnumerable bir değil ıcollection biz yinelerler tüm öğelerinde bir kullanırken gerekir iken ile bir MoveNext'in , .NET Framework Kod bir göz atın:

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;
}

Referans: Numaralandırılabilir Referans Kaynağı


2

Bunu anlamak için basit bir test yapabilirsiniz:

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;

TestCount ve testAny değerlerini kontrol edin.


1
Count property vs Any () Count property wins vs Any () ile kodu + 2x ile - link
Stanislav Prusac

1
Daha iyi bir sonuç için, bu karşılaştırmaları 1000 kez (veya daha fazla) yapabilirsiniz. Sonuçları ortalamaya ve rastgele ani artışlardan kaçınmaya yardımcı olur.
Roma

Yukarıda belirtilen yöntem gibi test yaparken, veritabanı / ağınızdaki yük, veritabanı tarafında önbellekleme planı, vb.
Vahid Farahmandian

daha iyi karşılaştırma için Countbir özellik değil, Count () ve .Any () yöntemiyle değiştirilmelidir. Yinelemeler için zamana ihtiyacınız var.
daremachine

0

Entity Framework kullanıyorsanız ve çok sayıda kayıt içeren büyük bir tablonuz varsa Any () çok daha hızlı olacaktır. Bir keresinde bir masanın boş olup olmadığını ve milyonlarca satırı olduğunu kontrol etmek istediğimi hatırlıyorum. Count ()> 0'ın tamamlanması 20-30 saniye sürdü. Any () ile anında oldu .

Any () bir performans geliştirme olabilir, çünkü bir şeyleri almak için koleksiyonu tekrar etmek zorunda kalmayabilir. Sadece bir tanesine çarpması gerekiyor. Ya da, örneğin, LINQ-to-Entities için oluşturulan SQL SELECT COUNT yerine IF EXISTS (...) veya SELECT * olacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.