.Any()と.Count()> 0のどちらの方がパフォーマンスが優れていますか?


578

System.Linq、名前空間、私たちは今、私たち拡張することができますIEnumerableをのを持っている(どれを)()カウント 拡張メソッドを

コレクション内に1つ以上のアイテムが含まれていることを確認する場合、拡張メソッドはすべてのアイテムを反復処理する必要があるため、.Any()拡張メソッドの代わりに拡張メソッドを使用する必要があると最近言われました。.Count() > 0.Count()

第2に、一部のコレクションには、または(拡張メソッドではなく)プロパティがあります。またはの代わりにそれらを使用する方が良いでしょうか?CountLength.Any().Count()

いや/いや?


EnumerablesでAny()を使用し、コレクションでCountを使用する方が良い。誰かが「(somecollection.Count> 0)」を書くと混乱したり、読みやすさの問題を引き起こしたりする場合は、Any()という名前の拡張メソッドとして記述してください。その後、誰もが満足しました。パフォーマンスと読みやすさの両方。そのため、すべてのコードに一貫性があり、プロジェクトの個々の開発者はCountとAnyの選択について心配する必要はありません。
Mahesh Bongani

回答:


709

あなたが持っている何かを開始する場合.Lengthまたは.Count(ようICollection<T>IList<T>List<T>、など) -それが通過する必要がないので、これは、最速のオプションになりますGetEnumerator()/ MoveNext()/ Dispose()で必要とされるシーケンスAny()空でないかどうかを確認するためIEnumerable<T>のシーケンス。

ちょうどのためにIEnumerable<T>、そしてAny()だろう一般的に、それが唯一の反復を見て持っているとして、より速くなります。しかし、LINQのツーオブジェクトの実装は、そのノートCount()のチェックを行いますICollection<T>(使用して.Count最適化として) -あなたの基礎となるデータ・ソースがある場合はその直接リスト/コレクション、大きな違いがありません。それが非ジェネリックを使用しない理由を私に尋ねないでくださいICollection...

もちろん、LINQを使用してフィルター処理などWhereを行っている場合(など)、イテレーターブロックベースのシーケンスがあるため、このICollection<T>最適化は役に立ちません。

一般的にIEnumerable<T>Any();-p


9
Marc:ICollection <T>は、実際にはICollectionから派生したものではありません。私もびっくりしましたが、リフレクターは嘘をつきません。
ブライアンワッツ

7
Any()実装はICollectionインターフェイスをチェックし、Countプロパティをチェックしませんか?
derigel 2009

313
ほとんどの場合Any()を使用する別の理由があると思います。開発者の意図を正確に伝えます。アイテムの数を知る必要はないが、アイテムの数がある場合にのみ、somecollection.Any()はsomecollection.Count> 0より単純で明確です
TJKjaer

13
@huttelihut-ステートメントに本当に混乱している開発者を何人知っていますか(somecollection.Count > 0)?LINQの.Any()メソッドが導入される前のすべてのコードは理解しづらかったですか?
CraigTP 2013年

25
@JLRishe-私は今でもそれsomeCollection.Count > 0が同じように明確someCollection.Any()であり、パフォーマンスが向上し、LINQを必要としないという追加の利点があると感じています。確かに、これは非常に単純なケースであり、LINQ演算子を使用する他の構成は、同等の非LINQオプションよりもはるかに明確に開発者の意図を伝えます。
CraigTP 14

65

注:この回答は、Entity Framework 4が実際にあったときに書きました。この回答のポイントは些細に入るためにはなかった.Any().Count()性能試験。ポイントは、EFが完璧とはほど遠いことを示すことでした。新しいバージョンの方が優れていますが、コードの一部に低速でEFを使用している場合は、前提条件に依存するのではなく、直接TSQLでテストし、パフォーマンスを比較します(.Any()常により高速です.Count() > 0)。


私は同意するものの、ほとんどの回答やコメントをアップでは、投票-特にポイントにAny信号の意思開発者よりも良いCount() > 0-私はカウントがSQL Serverの(EntityFramework 4)上の大きさの順に速くなっている状況がありました。

これはAny、タイムアウト例外(〜200.000レコード)を使用したクエリです。

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 ミリ秒単位で実行されるバージョン:

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

私は正確なSQLの両方LINQsが作り出すかを確認する方法を見つける必要がある-それは間に大きなパフォーマンスの差がある明らかだCountし、Anyいくつかのケースでは、残念ながらそれはあなただけに固執することはできませんと思われるAnyすべてのケースインチ

編集:生成されたSQLを以下に示します。あなたが見ることができる美しさ;)

ANY

exec sp_executesql N'SELECT TOP(1) 
[Project2]。[ContactId] AS [ContactId]、 
[Project2]。[CompanyId] AS [CompanyId]、 
[Project2]。[ContactName] AS [ContactName]、 
[Project2]。[FullName] AS [FullName]、 
[Project2]。[ContactStatusId] AS [ContactStatusId]、 
[Project2]。[Created] AS [Created]
FROM(SELECT [Project2]。[ContactId] AS [ContactId]、[Project2]。[CompanyId] AS [CompanyId]、[Project2]。[ContactName] AS [ContactName]、[Project2]。[FullName] AS [FullName] 、[Project2]。[ContactStatusId] AS [ContactStatusId]、[Project2]。[Created] AS [Created]、row_number()OVER(ORDER BY [Project2]。[ContactId] ASC)AS [row_number]
    FROM(SELECT 
        [Extent1]。[ContactId] AS [ContactId]、 
        [Extent1]。[CompanyId] AS [CompanyId]、 
        [Extent1]。[ContactName] AS [ContactName]、 
        [Extent1]。[FullName] AS [FullName]、 
        [Extent1]。[ContactStatusId] AS [ContactStatusId]、 
        [Extent1]。[Created] AS [Created]
        FROM [dbo]。[Contact] AS [Extent1]
        WHERE([Extent1]。[CompanyId] = @ p__linq__0)AND([Extent1]。[ContactStatusId] <= 3)AND(NOT EXISTS(SELECT 
            1 AS [C1]
            FROM [dbo]。[NewsletterLog] AS [Extent2]
            WHERE([Extent1]。[ContactId] = [Extent2]。[ContactId])AND(6 = [Extent2]。[NewsletterLogTypeId])
        ))
    )AS [Project2]
)AS [Project2]
WHERE [Project2]。[row_number]> 99
ORDER BY [Project2]。[ContactId] ASC '、N' @ p__linq__0 int '、@ p__linq__0 = 4

COUNT

exec sp_executesql N'SELECT TOP(1) 
[Project2]。[ContactId] AS [ContactId]、 
[Project2]。[CompanyId] AS [CompanyId]、 
[Project2]。[ContactName] AS [ContactName]、 
[Project2]。[FullName] AS [FullName]、 
[Project2]。[ContactStatusId] AS [ContactStatusId]、 
[Project2]。[Created] AS [Created]
FROM(SELECT [Project2]。[ContactId] AS [ContactId]、[Project2]。[CompanyId] AS [CompanyId]、[Project2]。[ContactName] AS [ContactName]、[Project2]。[FullName] AS [FullName] 、[Project2]。[ContactStatusId] AS [ContactStatusId]、[Project2]。[Created] AS [Created]、row_number()OVER(ORDER BY [Project2]。[ContactId] ASC)AS [row_number]
    FROM(SELECT 
        [Project1]。[ContactId] AS [ContactId]、 
        [Project1]。[CompanyId] AS [CompanyId]、 
        [Project1]。[ContactName] AS [ContactName]、 
        [Project1]。[FullName] AS [FullName]、 
        [Project1]。[ContactStatusId] AS [ContactStatusId]、 
        [Project1]。[Created] AS [Created]
        FROM(SELECT 
            [Extent1]。[ContactId] AS [ContactId]、 
            [Extent1]。[CompanyId] AS [CompanyId]、 
            [Extent1]。[ContactName] AS [ContactName]、 
            [Extent1]。[FullName] AS [FullName]、 
            [Extent1]。[ContactStatusId] AS [ContactStatusId]、 
            [Extent1]。[Created] AS [Created]、 
            (選択する 
                COUNT(1)AS [A1]
                FROM [dbo]。[NewsletterLog] AS [Extent2]
                WHERE([Extent1]。[ContactId] = [Extent2]。[ContactId])AND(6 = [Extent2]。[NewsletterLogTypeId]))AS [C1]
            FROM [dbo]。[Contact] AS [Extent1]
        )AS [Project1]
        WHERE([Project1]。[CompanyId] = @ p__linq__0)AND([Project1]。[ContactStatusId] <= 3)AND(0 = [Project1]。[C1])
    )AS [Project2]
)AS [Project2]
WHERE [Project2]。[row_number]> 99
ORDER BY [Project2]。[ContactId] ASC '、N' @ p__linq__0 int '、@ p__linq__0 = 4

純粋なWhere with EXISTSは、Countを計算してからWhere = Count == 0を実行するよりもはるかに悪いようです。

私の調査結果にエラーがあったら教えてください。Any vs Countの議論に関係なく、これからすべてを取り除くことができるのは、より複雑なLINQは、ストアドプロシージャとして書き換えた方がずっとよいことです;)。


2
各シナリオの各linq-queryによって生成されるいくつかのSQLクエリプランを見たいです。
Pure.Krome

43
SQLに基づいて、私が言えることは、両方のクエリが恐ろしく見えることです。私が通常自分でTSQLを作成する理由があることは知っていました...
Marc Gravell

!Countと同じように、すべての行をすべての行で確認する必要があります。あなたの例がそのような恐ろしい結果を与えることは、最悪の場合、少し奇妙です!AnyはCountより少しだけ遅くなるはずです。あなたの場合、選択を単純化する方法を探します。可能であれば、段階的に分割するか、条件を並べ替えます。しかし、AnyはCountよりも優れているという規則は成り立たない!AnyはCountより優れており、非常に優れています。
ベント

25

これはかなり人気のあるトピックであり、答えが異なるため、問題を再検討する必要がありました。

テスト環境: EF 6.1.3、SQL Server、300kレコード

テーブルモデル

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

    public string Name { get; set; }

    public string Surname { get; set; }
}

テストコード:

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

結果:

Any()〜3ミリ秒

Count()〜最初のクエリで230ミリ秒、2番目のクエリで〜400ミリ秒

備考:

私の場合、EFは彼の投稿で言及されている@BenのようなSQLを生成しませんでした。


4
適切に比較するには、を実行する必要がありますCount() > 0。:D
Andrew

1
アンドリュー、Count()> 0は、この特定のテストではCount()と異なる動作をしません。
CodeMonkeyForHire 2018

11

編集: EFバージョン6.1.1で修正されました。そしてこの答えはもはや実際ではありません

SQL ServerとEF4-6の場合、Count()はAny()よりも約2倍速く実行されます。

Table.Any()を実行すると、次のようなものが生成されます(警告:理解しようとする脳を傷つけないでください

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]

条件付きの行を2回スキャンする必要があります。

Count() > 0私の意図が隠されているので、私は書くのが好きではありません。私はこれにカスタム述語を使用することを好みます:

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

私もこれに気づきました。Any()SQLはまったく意味がありません。彼らがしない理由はわかりません:CASE WHEN(EXISTS(sql))THEN 1 ELSE 0 END。0を返すためにNOT EXISTSを実行する必要がある理由は考えられません
。– scott.korin

これは誤りです。偶然に間違ったクエリプランを見つけました。これが起こります。ほとんどの場合、どれも高速です。
usr

私は6.1.3で生成されたSQLをチェックし、修正しました:SELECT CASE WHEN(EXISTS(SELECT 1 AS [C1] FROM [dbo]。[TestTables] AS [Extent1] WHERE [Extent1]。[Id]> 1000)) THEN cast(1 as bit)ELSE cast(0 as bit)END AS [C1] FROM(SELECT 1 AS X)AS [SingleRowTable1]
Ben

6

それはデータセットの大きさとパフォーマンス要件は何ですか?

それが巨大でない場合は、最も読みやすい形式を使用します。これは、方程式よりも短くて読みやすいので、私にとっては任意の形式です。


2

およそカウント()場合の方法、IEnumarableがあるICollectionを、我々はすべての項目間で反復処理することはできません我々は取得することができますので、カウントのフィールドICollectionをする場合は、IEnumerableをはないICollectionを我々が使用して、すべての項目間で反復しなければなりませんしばらくしてMoveNextメソッド、.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;
}

参照:参照ソース列挙可能


2

これを理解する簡単なテストを行うことができます:

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とtestAnyの値を確認します。


1
これは、Countプロパティvs Any()のコードを使用したテストで、CountプロパティwinsとAny()は+ 2xでテストされます- リンク
Stanislav Prusac

1
より良い結果を得るには、これらの比較を1000回(またはそれ以上)行うことができます。結果を平均化し、ランダムなスパイクを回避するのに役立ちます。
ローマ

上記の方法のようにテストする場合は、データベース/ネットワークの負荷、データベース側でのキャッシュの計画など、さらに多くの要素を考慮する必要があります。したがって、正確なテストを行うには、分離された正確な環境も設計する必要があります。
Vahid Farahmandian 2018

より良い比較のためにCount、プロパティではなくCount()メソッドと.Any()メソッドで置き換える必要があります。反復の時間が必要です。
daremachine

0

Entity Frameworkを使用していて、多数のレコードを持つ巨大なテーブルがある場合、Any()の方がはるかに高速になります。テーブルが空で何百万行もあるかどうかを確認したかったことを覚えています。Count()> 0が完了するまでに20〜30秒かかりました。Any()ですぐにできました。

Any()は、コレクションを繰り返し処理する必要がないため、パフォーマンスを向上させることができます。それらの1つをヒットする必要があります。または、たとえば、LINQ-to-Entitiesの場合、生成されるSQLはSELECT COUNT ...またはSELECT * ...ではなくIF EXISTS(...)になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.