Вопрос

Какой "лучший" (с учетом скорости и удобочитаемости) способ определить, пуст ли список?Даже если список имеет тип 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();
}
Это было полезно?

Решение

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

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

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

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

Я хотел бы сделать одно небольшое дополнение к коду, на котором вы, похоже, остановились: проверьте также на 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();
}
  

LINQ должен как-то серьезно оптимизировать метод Count ().

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

/ EDIT @Matt:

  

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

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

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

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

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

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

Думаю, это зависит от типа используемого вами списка!

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

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

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

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

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

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

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

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

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

Вы привыкаете использовать Любой из них - он действительно имеет смысл и удобочитаем.

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

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

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

/ EDIT @Konrad +1 mate - вы правы в том, что вы, скорее всего, находитесь на <=>.

Хорошо, а как насчет этого?

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

РЕДАКТИРОВАТЬ: я только что понял, что кто-то уже набросал это решение. Было упомянуто, что метод Any () сделает это, но почему бы не сделать это самостоятельно? Отношения

Еще одна идея:

if(enumerable.FirstOrDefault() != null)

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

Это было важно, чтобы заставить это работать с Entity Framework:

var genericCollection = list as ICollection<T>;

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

Если я проверяю с помощью 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)
{
    //....
}
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);
    }

Вот моя реализация ответа Дэна Тао с учетом предиката:

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

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

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

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top