Вопрос

Я ищу лучшую альтернативу для 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));
    }
}

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

Это было полезно?

Решение

Вы можете использовать комбинацию 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;
}

Другие советы

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

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

int count = 0;
foreach (var item in items)
{
    count++;
    if(count > n)
        return false;
}
return count == n;

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top