alternatives à Enumerable.Count () == n
-
28-10-2019 - |
Question
Je cherche une meilleure alternative pour Enumerable.Count() == n
. Le meilleur que je suis en mesure de trouver est:
static class EnumerableExtensions
{
public static bool CountEquals<T>(this IEnumerable<T> items, int n)
{
if (n <= 0) throw new ArgumentOutOfRangeException("n"); // use Any()
var iCollection = items as System.Collections.ICollection;
if (iCollection != null)
return iCollection.Count == n;
int count = 0;
bool? retval = null;
foreach (var item in items)
{
count++;
if (retval.HasValue)
return false;
if (count == n)
retval = true;
}
if (retval.HasValue)
return retval.Value;
return false;
}
}
class Program
{
static void Main(string[] args)
{
var items0 = new List<int>();
var items1 = new List<int>() { 314 };
var items3 = new List<int>() { 1, 2, 3 };
var items5 = new List<int>() { 1, 2, 3, 4, 5 };
var items10 = Enumerable.Range(0, 10);
var itemsLarge = Enumerable.Range(0, Int32.MaxValue);
Console.WriteLine(items0.CountEquals(3));
Console.WriteLine(items1.CountEquals(3));
Console.WriteLine(items3.CountEquals(3));
Console.WriteLine(items5.CountEquals(3));
Console.WriteLine(itemsLarge.CountEquals(3));
}
}
Puis-je faire mieux? Est-il possible de généraliser ce encore plus dans le passage-comparision?
La solution
Vous pouvez utiliser une combinaison de Take
et Count
pour se débarrasser de la boucle entièrement:
public static bool CountEquals<T>(this IEnumerable<T> items, int n)
{
var iCollection = items as System.Collections.ICollection;
if (iCollection != null)
return iCollection.Count == n;
return items.Take(n + 1).Count() == n;
}
Autres conseils
L'utilisation Enumerable.Count
serait beaucoup mieux que votre code ci-dessus. Il optimise déjà ICollection
en interne.
Cela étant dit, si vous devez garder votre poste, vous pouvez simplifier la boucle un peu:
int count = 0;
foreach (var item in items)
{
count++;
if(count > n)
return false;
}
return count == n;
Que voulez-vous dire exactement par "mieux" ? Plus rapide? Plus facile?
Essentiellement ce que vous semblez avoir fait est écrit une méthode spécialisée qui est optimisé pour une tâche spécifique. Vous mentionnez le généralisant, mais son avantage de performance provient du fait qu'il est si spécifique (en supposant qu'il un avantage de performance - des méthodes telles que Count
sont déjà réglés assez difficile pour la performance, et très bon du compilateur à l'optimisation des trucs comme ça).
L'optimisation prématurée est la racine de tous les maux. Si l'exécution de cette opération particulière est si important que cela vaut la peine de remplacer l'expression caractère vingt-impair xyz.Count() == abc
avec des dizaines de lignes de code, vous pouvez essayer d'autres méthodes de l'augmentation des performances, telles que le refactoring. Pour la plupart des situations, juste les frais généraux de l'utilisation de code managé va rapetisser la prime de rendement que vous obtenez (le cas échéant).
Cela étant dit - si vous avez quelque chose comme 10 millions d'articles, et votre nombre cible est beaucoup moins, je suis sûr le ci-dessous court-circuiter l'itération:
int count = 0;
var subset = items.TakeWhile(x => count++ < n + 1);
return count == n + 1;
Facile à lire, facile à entretenir, et probablement tout aussi rapide.