альтернативы Enumerable.Count () == n - PullRequest
1 голос
/ 02 февраля 2011

Я ищу лучшую альтернативу для Enumerable.Count() == n. Лучшее, что я смог придумать, это:

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

Могу ли я сделать что-нибудь лучше? Есть ли способ обобщить это еще больше - прохождение сравнения?

Ответы [ 3 ]

2 голосов
/ 02 февраля 2011

Вы можете использовать комбинацию Take и Count, чтобы полностью избавиться от цикла:

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;
}
2 голосов
/ 02 февраля 2011

Использование Enumerable.Count будет намного лучше, чем ваш код выше. Он уже оптимизирован для ICollection внутри.

При этом, если вам нужно сохранить расширение, вы можете немного упростить цикл:

int count = 0;
foreach (var item in items)
{
    count++;
    if(count > n)
        return false;
}
return count == n;
0 голосов
/ 02 февраля 2011

Что именно вы подразумеваете под "лучше" ? Быстрее? Полегче?

По сути, похоже, что вы написали специальный метод, оптимизированный для одной конкретной задачи. Вы упомянули обобщение, но его преимущество в производительности проистекает из того факта, что он настолько специфичен (если предположить, что - это преимущество в производительности - такие методы, как Count, уже достаточно сильно настроены для производительности, а компилятор довольно хорош на оптимизацию, как это).

Преждевременная оптимизация - корень всего зла. Если выполнение этой конкретной операции настолько важно, что стоит заменить выражение из двадцати с лишним символов xyz.Count() == abc десятками строк кода, вы Возможно, вы захотите попробовать другие методы повышения производительности, такие как рефакторинг. В большинстве случаев только издержки от использования управляемого кода приведут к уменьшению получаемого бонуса производительности (если есть).

При этом - если у вас есть что-то вроде 10 миллионов предметов, и количество ваших целей намного меньше, я уверен, что нижеприведенное закоротит итерацию:

int count = 0;
var subset = items.TakeWhile(x => count++ < n + 1);
return count == n + 1;

Легко читать, легко обслуживать и, вероятно, так же быстро.

...