Quelles sont les principales différences entre IEnumerable
Count()
et Length
?
En appelant Count on IEnumerable<T>
Je suppose que vous faites référence à la méthode d'extension Count
sur System.Linq.Enumerable
. Length
n'est pas une méthode sur IEnumerable<T>
mais plutôt une propriété sur les types de tableaux dans .Net tels que int[]
.
La différence est la performance. La propriété Length
est garantie d'être une opération O(1). La complexité de la méthode d'extension Count
varie en fonction du type d'exécution de l'objet. Elle sera essayez de transtyper en plusieurs types qui prennent en charge O(1) recherche de longueur comme ICollection<T>
via une propriété Count
. Si aucun n'est disponible, il énumérera tous les éléments et les comptera, ce qui a une complexité de O (N).
Par exemple
int[] list = CreateSomeList();
Console.WriteLine(list.Length); // O(1)
IEnumerable<int> e1 = list;
Console.WriteLine(e1.Count()); // O(1)
IEnumerable<int> e2 = list.Where(x => x <> 42);
Console.WriteLine(e2.Count()); // O(N)
La valeur e2
est implémenté comme un itérateur C # qui ne prend pas en charge le comptage O(1)) et donc la méthode Count
doit énumérer la collection entière pour déterminer sa durée.
Petit ajout à Jon Skeet 's comment.
Voici le code source de la méthode d'extension Count()
:
.NET 3:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
ICollection<TSource> is2 = source as ICollection<TSource>;
if (is2 != null)
{
return is2.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}
.NET 4:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
ICollection<TSource> is2 = source as ICollection<TSource>;
if (is2 != null)
{
return is2.Count;
}
ICollection is3 = source as ICollection;
if (is3 != null)
{
return is3.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}
La longueur est une propriété fixe, par ex. d'un tableau ou d'une chaîne unidimensionnelle. Il n'y a donc jamais d'opération de comptage nécessaire (les tableaux multidimensionnels ont une taille de toutes les dimensions multipliée). O(1) opération ici signifie que le temps de récupération est toujours le même, peu importe le nombre d'éléments. Une recherche linéaire serait (contrairement à cela) être O (n).
La propriété Count sur ICollections (List et List <T>, par exemple) peut changer, elle doit donc être mise à jour lors des opérations d'ajout/suppression, ou lorsque Count est demandé après la modification de la collection. Dépend de l'implémentation de l'objet.
La méthode Count () de LINQ itère essentiellement CHAQUE FOIS qu'il est appelé (sauf lorsque l'objet est de type ICollection, alors la propriété ICollection.Count est demandée).
Notez que IEnumerables ne sont souvent pas des collections d'objets déjà définies (comme des listes, des tableaux, des tables de hachage, etc.), mais des liens vers des opérations d'arrière-plan, qui génèrent des résultats chaque fois qu'elles sont demandées (appelées exécution différée).
En règle générale, vous avez une instruction SQL comme LINQ comme celle-ci (l'application typique de l'exécution différée):
IEnumerable<Person> deptLeaders =
from p in persons
join d in departments
on p.ID equals d.LeaderID
orderby p.LastName, p.FirstName
select p;
Ensuite, il y a du code comme celui-ci:
if (deptLeaders.Count() > 0)
{
ReportNumberOfDeptLeaders(deptLeaders.Count());
if (deptLeaders.Count() > 20)
WarnTooManyDepartmentLeaders(deptLeaders.Count());
}
Ainsi, lorsqu'un avertissement pour trop de chefs de département est émis, .NET passe QUATRE FOIS par le biais des personnes, les compare aux chefs de département, les trie par nom, puis compte les objets de résultat.
Et ce n'est que lorsque les personnes et les services sont des collections de valeurs prédéfinies, pas des requêtes elles-mêmes.