Проверка наличия пустого списка в LINQ

Что означает "лучший" (учитывая скорость и читаемость), чтобы определить, пуст ли список? Даже если список имеет тип IEnumerable<T> и не имеет свойства Count.

Сейчас я подбрасываю между этим:

if (myList.Count() == 0) { ... }

и это:

if (!myList.Any()) { ... }

Я предполагаю, что второй вариант быстрее, так как он вернется с результатом, как только он увидит первый элемент, тогда как второй вариант (для IEnumerable) должен будет посетить каждый элемент, чтобы вернуть счет.

Как говорится, второй вариант выглядит как читаемый для вас? Что бы вы предпочли? Или вы можете придумать лучший способ проверить пустой список?

Изменить @lassevk ответ кажется наиболее логичным, в сочетании с небольшим количеством проверок времени, чтобы использовать кешированный счет, если это возможно:

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;

    return !list.Any();
}

Ответ 1

Вы можете сделать это:

public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
    if (source == null)
        return true; // or throw an exception
    return !source.Any();
}

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

Ответ 2

Я бы сделал одно небольшое дополнение к коду, который, как вам кажется, решил: проверьте также на ICollection, поскольку это реализовано даже некоторыми не устаревшими родовыми классами (т.е. Queue<T> и Stack<T>). Я бы использовал as вместо is, поскольку он более идиоматичен и был показан быстрее.

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
    if (list == null)
    {
        throw new ArgumentNullException("list");
    }

    var genericCollection = list as ICollection<T>;
    if (genericCollection != null)
    {
        return genericCollection.Count == 0;
    }

    var nonGenericCollection = list as ICollection;
    if (nonGenericCollection != null)
    {
        return nonGenericCollection.Count == 0;
    }

    return !list.Any();
}

Ответ 3

LINQ сам по себе должен сделать некоторую серьезную оптимизацию вокруг метода Count().

Вас это удивляет? Я полагаю, что для IList реализаций Count просто считывает количество элементов непосредственно, а Any должен запрашивать метод IEnumerable.GetEnumerator, создавая экземпляр и вызывающий MoveNext хотя бы один раз.

/EDIT @Matt:

Я могу только предположить, что метод расширения Count() для IEnumerable делает что-то вроде этого:

Да, конечно. Это то, что я имел в виду. На самом деле он использует ICollection вместо IList, но результат тот же.

Ответ 4

Я только что написал быстрый тест, попробуйте следующее:

 IEnumerable<Object> myList = new List<Object>();

 Stopwatch watch = new Stopwatch();

 int x;

 watch.Start();
 for (var i = 0; i <= 1000000; i++)
 {
    if (myList.Count() == 0) x = i; 
 }
 watch.Stop();

 Stopwatch watch2 = new Stopwatch();

 watch2.Start();
 for (var i = 0; i <= 1000000; i++)
 {
     if (!myList.Any()) x = i;
 }
 watch2.Stop();

 Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString());
 Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString());
 Console.ReadLine();

Второй почти в три раза медленнее:)

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

Итак, я думаю, это зависит от типа списка, который вы используете!

(Только для того, чтобы указать, я помещал 2000+ объектов в список, и количество было еще быстрее, напротив других типов)

Ответ 5

@Konrad меня удивляет тем, что в моих тестах я передаю список методу, который принимает IEnumerable<T>, поэтому среда выполнения не может оптимизировать его, вызывая метод расширения Count() для IList<T>.

Я могу только предположить, что метод расширения Count() для IEnumerable делает что-то вроде этого:

public static int Count<T>(this IEnumerable<T> list)
{
    if (list is IList<T>) return ((IList<T>)list).Count;

    int i = 0;
    foreach (var t in list) i++;
    return i;
}

... Другими словами, небольшая оптимизация времени выполнения для специального случая IList<T>.

/EDIT @Konrad +1 mate - вы правы в этом, скорее, на ICollection<T>.

Ответ 6

List.Count - O (1) согласно документации Microsoft:
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

поэтому просто используйте List.Count == 0 намного быстрее, чем запрос

Это потому, что у него есть элемент данных Count, который обновляется в любое время, когда что-то добавляется или удаляется из списка, поэтому, когда вы вызываете List.Count, ему не нужно перебирать каждый элемент, чтобы получить его, это просто возвращает элемент данных.

Ответ 7

Второй вариант намного быстрее, если у вас несколько элементов.

  • Any() возвращает, как только будет найден один элемент.
  • Count() должен продолжать просматривать весь список.

Например, предположим, что перечисление имеет 1000 элементов.

  • Any() проверит первый, затем вернет true.
  • Count() вернет 1000 после прохождения всей нумерации.

Это потенциально хуже, если вы используете один из предикатов - Count() все еще должен проверять каждый элемент, даже если есть только одно совпадение.

Вы привыкаете к использованию Any - это имеет смысл и читается.

Одно предостережение - если у вас есть список, а не только IEnumerable, то используйте свойство Count.

Ответ 8

Итак, как насчет этого?

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    return !enumerable.GetEnumerator().MoveNext();
}

EDIT: Я только понял, что кто-то уже набросал это решение. Было упомянуто, что метод Any() сделает это, но почему бы не сделать это самостоятельно? Отношения

Ответ 9

Другая идея:

if(enumerable.FirstOrDefault() != null)

Однако мне больше нравится подход Any().

Ответ 10

Это было важно для работы с Entity Framework:

var genericCollection = list as ICollection<T>;

if (genericCollection != null)
{
   //your code 
}

Ответ 11

Если я проверил с Count() Linq выполняет "SELECT COUNT (*).." в базе данных, но мне нужно проверить, содержат ли результаты данные, я решил ввести FirstOrDefault() вместо Count();

Перед

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

if (cfop.Count() > 0)
{
    var itemCfop = cfop.First();
    //....
}

После

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

var itemCfop = cfop.FirstOrDefault();

if (itemCfop != null)
{
    //....
}

Ответ 12

private bool NullTest<T>(T[] list, string attribute)

    {
        bool status = false;
        if (list != null)
        {
            int flag = 0;
            var property = GetProperty(list.FirstOrDefault(), attribute);
            foreach (T obj in list)
            {
                if (property.GetValue(obj, null) == null)
                    flag++;
            }
            status = flag == 0 ? true : false;
        }
        return status;
    }


public PropertyInfo GetProperty<T>(T obj, string str)

    {
        Expression<Func<T, string, PropertyInfo>> GetProperty = (TypeObj, Column) => TypeObj.GetType().GetProperty(TypeObj
            .GetType().GetProperties().ToList()
            .Find(property => property.Name
            .ToLower() == Column
            .ToLower()).Name.ToString());
        return GetProperty.Compile()(obj, str);
    }

Ответ 13

List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;

Ответ 14

Здесь моя реализация ответа Дана Дао, позволяющая использовать предикат:

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null) throw new ArgumentNullException();
    if (IsCollectionAndEmpty(source)) return true;
    return !source.Any(predicate);
}

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException();
    if (IsCollectionAndEmpty(source)) return true;
    return !source.Any();
}

private static bool IsCollectionAndEmpty<TSource>(IEnumerable<TSource> source)
{
    var genericCollection = source as ICollection<TSource>;
    if (genericCollection != null) return genericCollection.Count == 0;
    var nonGenericCollection = source as ICollection;
    if (nonGenericCollection != null) return nonGenericCollection.Count == 0;
    return false;
}

Ответ 15

myList.ToList().Count == 0. Что все

Ответ 16

Этот метод расширения работает для меня:

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}