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?

Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top