122 votes

Vérifier si une liste est vide avec LINQ

Quelle est la "meilleure" façon (en tenant compte à la fois de la vitesse et de la lisibilité) de déterminer si une liste est vide ? Même si la liste est de type IEnumerable<T> et ne possède pas de propriété Count.

En ce moment, j'hésite entre ça :

if (myList.Count() == 0) { ... }

et ceci :

if (!myList.Any()) { ... }

Je pense que la seconde option est plus rapide, puisqu'elle renverra un résultat dès qu'elle verra le premier élément, alors que la seconde option (pour un IEnumerable) devra visiter chaque élément pour renvoyer le compte.

Cela dit, la deuxième option vous semble-t-elle aussi lisible ? Laquelle préférez-vous ? Ou pouvez-vous imaginer une meilleure façon de tester une liste vide ?

Modifier La réponse de @lassevk semble être la plus logique, couplée à un peu de vérification à l'exécution pour utiliser un compte en cache si possible, comme ceci :

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;

    return !list.Any();
}

100voto

Lasse V. Karlsen Points 148037

Tu pourrais faire ça :

public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
    if (source == null)
        return true; // or throw an exception
    return !source.Any();
}

Modifier : Notez que la simple utilisation de la méthode .Count sera rapide si la source sous-jacente possède effectivement une propriété Count rapide. Une optimisation valable ci-dessus serait de détecter quelques types de base et d'utiliser simplement la propriété .Count de ceux-ci, au lieu de l'approche .Any(), mais de revenir à .Any() si aucune garantie ne peut être faite.

14voto

Dan Tao Points 60518

Je ferais un petit ajout au code que vous semblez avoir adopté : vérifiez également la présence de ICollection car cela est également mis en œuvre par certaines classes génériques non obsolètes (c'est-à-dire, Queue<T> et Stack<T> ). J'utiliserais également as au lieu de is car c'est plus idiomatique et s'est avéré plus rapide .

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
    }

    var genericCollection = list as ICollection<T>;
    if (genericCollection != null)
    {
        return genericCollection.Count == 0;
    }

    var nonGenericCollection = list as ICollection;
    if (nonGenericCollection != null)
    {
        return nonGenericCollection.Count == 0;
    }

    return !list.Any();
}

8voto

Konrad Rudolph Points 231505

LINQ lui-même doit faire une sérieuse optimisation autour de la méthode Count() d'une manière ou d'une autre.

Cela vous surprend-il ? J'imagine que pour IList mises en œuvre, Count lit simplement le nombre d'éléments directement tandis que Any doit interroger le IEnumerable.GetEnumerator créez une instance et appelez MoveNext au moins une fois.

/EDIT @ Matt :

Je ne peux que supposer que la méthode d'extension Count() pour IEnumerable fait quelque chose comme ça :

Oui, bien sûr. C'est ce que je voulais dire. En fait, il utilise ICollection au lieu de IList mais le résultat est le même.

6voto

crucible Points 1712

Je viens d'écrire un test rapide, essayez ça :

 IEnumerable<Object> myList = new List<Object>();

 Stopwatch watch = new Stopwatch();

 int x;

 watch.Start();
 for (var i = 0; i <= 1000000; i++)
 {
    if (myList.Count() == 0) x = i; 
 }
 watch.Stop();

 Stopwatch watch2 = new Stopwatch();

 watch2.Start();
 for (var i = 0; i <= 1000000; i++)
 {
     if (!myList.Any()) x = i;
 }
 watch2.Stop();

 Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString());
 Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString());
 Console.ReadLine();

Le second est presque trois fois plus lent :)

En essayant à nouveau le test du chronomètre avec une pile ou un tableau ou d'autres scénarios, cela dépend vraiment du type de liste, semble-t-il, car ils s'avèrent être plus lents.

Je suppose donc que cela dépend du type de liste que vous utilisez !

(Juste pour préciser, j'ai mis 2000+ objets dans la liste et le comptage était toujours plus rapide, contrairement aux autres types)

3voto

Keith Points 46288

La deuxième option est beaucoup plus rapide si vous avez plusieurs articles.

  • Any() revient dès qu'un élément est trouvé.
  • Count() doit continuer à parcourir la liste entière.

Supposons par exemple que l'énumération comporte 1000 éléments.

  • Any() vérifierait le premier, puis retournerait vrai.
  • Count() retournerait 1000 après avoir parcouru toute l'énumération.

C'est potentiellement pire si vous utilisez l'une des surcharges de prédicat - Count() doit toujours vérifier chaque élément, même s'il n'y a qu'une seule correspondance.

On s'habitue à utiliser l'autre - elle a un sens et est lisible.

Une mise en garde : si vous avez une liste, plutôt qu'un simple IEnumerable, utilisez la propriété Count de cette liste.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X