Question Quelle méthode fonctionne le mieux: .Any () vs .Count ()> 0?


dans le System.Linq espace de noms, nous pouvons maintenant étendre notre IEnumerableest d'avoir leAny() et Count()  méthodes d'extension.

On m'a dit récemment que si je veux vérifier qu'une collection contient un ou plusieurs éléments à l'intérieur, je devrais utiliser le .Any() méthode d'extension au lieu de la .Count() > 0 méthode d'extension parce que le .Count() La méthode d'extension doit parcourir tous les éléments.

Deuxièmement, certaines collections ont un propriété (pas une méthode d'extension) qui est Count ou Length. Serait-il préférable de les utiliser, au lieu de .Any() ou .Count() ?

oui / non?


490
2017-11-20 12:11


origine


Réponses:


Si vous commencez avec quelque chose qui a un .Length ou .Count (tel que ICollection<T>, IList<T>, List<T>, etc) - alors ce sera l'option la plus rapide, car il n'a pas besoin de passer par le GetEnumerator()/MoveNext()/Dispose() séquence requise par Any() pour vérifier un non-vide IEnumerable<T> séquence.

Pour seulement IEnumerable<T>, puis Any() volonté généralement être plus rapide, car il suffit de regarder une itération. Cependant, notez que l'implémentation LINQ-to-Objects de Count() vérifie pour ICollection<T> (en utilisant .Count comme une optimisation) - donc si votre source de données sous-jacente est directement une liste / collection, il n'y aura pas une énorme différence. Ne me demandez pas pourquoi il n'utilise pas le non-générique ICollection...

Bien sûr, si vous avez utilisé LINQ pour le filtrer, etc. (Where etc), vous aurez une séquence basée sur un bloc itérateur, et donc ICollection<T> l'optimisation est inutile.

En général avec IEnumerable<T> : coller avec Any() ;-p


597
2017-11-20 12:37



Remarque: J'ai écrit cette réponse quand Entity Framework 4 était réel. Le but de cette réponse était de ne pas entrer dans la banalité .Any() contre .Count() Test de performance. Le but était de signaler que EF est loin d'être parfait. Les versions plus récentes sont meilleures ... mais si vous avez une partie du code qui est lente et utilise EF, testez directement avec TSQL et comparez les performances plutôt que de vous baser sur des hypothèses (que .Any() est TOUJOURS plus rapide que .Count() > 0).


Alors que je suis d'accord avec la plupart des réponses et des commentaires votés - en particulier sur le point Any signaux intention du développeur mieux que Count() > 0 - J'ai eu une situation dans laquelle Count est plus rapide par ordre de grandeur sur SQL Server (EntityFramework 4).

Voici une requête avec Any cette exception de délai d'attente (sur ~ 200 000 enregistrements):

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 version exécutée en quelques millisecondes:

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

Je dois trouver un moyen de voir ce que les deux LINQ produisent exactement, mais il est évident qu'il y a une énorme différence de performance entre Count et Any dans certains cas, et malheureusement, il semble que vous ne pouvez pas rester avec Any dans tous les cas.

EDIT: Voici les SQL générés. Beautés comme vous pouvez le voir;)

ANY:

exec sp_executesql N'SELECT TOP (1)
[Projet2]. [ContactId] AS [ContactId],
[Project2]. [CompanyId] AS [CompanyId],
[Project2]. [ContactName] AS [ContactName],
[Projet2]. [Nom complet] AS [Nom complet],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Projet2]. [Créé] AS [Créé]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Créé] AS [Créé], 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 [Nom complet],
        [Extent1]. [ContactStatusId] AS [ContactStatusId],
        [Extent1]. [Créé] AS [Créé]
        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 [Étendue2]
            WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) ET (6 = [Extent2]. [NewsletterLogTypeId])
        ))
    ) AS [Projet2]
) AS [Projet2]
WHERE [Project2]. [Row_number]> 99
COMMANDER PAR [Project2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

COUNT:

exec sp_executesql N'SELECT TOP (1)
[Projet2]. [ContactId] AS [ContactId],
[Project2]. [CompanyId] AS [CompanyId],
[Project2]. [ContactName] AS [ContactName],
[Projet2]. [Nom complet] AS [Nom complet],
[Project2]. [ContactStatusId] AS [ContactStatusId],
[Projet2]. [Créé] AS [Créé]
FROM (SELECT [Project2]. [ContactId] AS [ContactId], [Project2]. [CompanyId] AS [CompanyId], [Project2]. [ContactName] AS [ContactName], [Project2]. [FullName] AS [FullName] , [Project2]. [ContactStatusId] AS [ContactStatusId], [Project2]. [Créé] AS [Créé], row_number () OVER (ORDER BY [Project2]. [ContactId] ASC) AS [row_number]
    FROM (SELECT
        [Project1]. [ContactId] AS [ContactId],
        [Projet1]. [CompanyId] AS [CompanyId],
        [Project1]. [ContactName] AS [ContactName],
        [Projet1]. [Nom complet] AS [Nom complet],
        [Project1]. [ContactStatusId] AS [ContactStatusId],
        [Projet1]. [Créé] AS [Créé]
        FROM (SELECT
            [Extent1]. [ContactId] AS [ContactId],
            [Extent1]. [CompanyId] AS [CompanyId],
            [Extent1]. [ContactName] AS [ContactName],
            [Extent1]. [FullName] AS [Nom complet],
            [Extent1]. [ContactStatusId] AS [ContactStatusId],
            [Extent1]. [Créé] AS [Créé],
            (SÉLECTIONNER
                COUNT (1) AS [A1]
                FROM [dbo]. [NewsletterLog] AS [Étendue2]
                WHERE ([Extent1]. [ContactId] = [Extent2]. [ContactId]) ET (6 = [Extent2]. [NewsletterLogTypeId])) AS [C1]
            FROM [dbo]. [Contact] AS [Extent1]
        ) AS [Projet1]
        OERE ([Project1]. [CompanyId] = @ p__linq__0) ET ([Project1]. [ContactStatusId] <= 3) AND (0 = [Projet1]. [C1])
    ) AS [Projet2]
) AS [Projet2]
WHERE [Project2]. [Row_number]> 99
COMMANDER PAR [Project2]. [ContactId] ASC ', N' @ p__linq__0 int ', @ p__linq__0 = 4

Semble que le pur Où avec EXISTS fonctionne bien pire que le calcul de Count, puis de faire Où avec Count == 0.

Faites-moi savoir si vous voyez une erreur dans mes résultats. Ce qui peut être retiré de tout cela indépendamment de la discussion Any vs Count est que tout LINQ plus complexe est bien mieux lorsqu'il est réécrit en tant que procédure stockée;).


53
2018-06-14 23:22



Comme ce sujet est plutôt populaire et que les réponses diffèrent, j'ai dû jeter un nouveau regard sur le problème.

Test env: EF 6.1.3, SQL Server, enregistrements 300k

Modèle de table:

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

    public string Name { get; set; }

    public string Surname { get; set; }
}

Code de test:

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

Résultats:

Tout () ~ 3ms

Count () ~ 230ms pour la première requête, ~ 400ms pour la seconde

Remarques:

Pour mon cas, EF n'a pas généré de SQL comme @Ben mentionné dans son post.


15
2018-05-28 08:14



MODIFIER: il a été corrigé dans la version EF 6.1.1. et cette réponse n'est plus réelle

Pour SQL Server et EF4-6, Count () est environ deux fois plus rapide que Any ().

Lorsque vous exécutez Table.Any (), il générera quelque chose comme (alerte: ne pas blesser le cerveau en essayant de le comprendre)

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]

cela nécessite 2 balayages de lignes avec votre condition.

Je n'aime pas écrire Count() > 0 parce que cela cache mon intention. Je préfère utiliser un prédicat personnalisé pour cela:

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

9
2018-01-03 16:32



Eh bien, le .Count() la méthode d'extension n'utilisera pas le .Count propriété, mais je suppose que vous n'utiliseriez pas le .Count() méthode pour une collection simple, mais plutôt à la fin d'une instruction LINQ avec des critères de filtrage, etc.

Dans ce contexte, .Any() sera plus rapide que .Count() > 0.


6
2017-11-20 12:13



Cela dépend, quelle est la taille de l'ensemble de données et quelles sont vos exigences de performance?

Si ce n'est rien de gigantesque, utilisez la forme la plus lisible, ce qui est pour moi, parce que c'est plus court et plus lisible qu'une équation.


4
2017-12-22 23:43



Vous pouvez faire un test simple pour comprendre cela:

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;

Vérifiez les valeurs de testCount et testAny.


1
2018-01-20 17:15