2008-09-03 6 views
121

Что такое «лучший» (с учетом скорости и удобочитаемости), чтобы определить, пуст ли список? Даже если список имеет тип IEnumerable<T> и не имеет свойства Count.Проверка наличия пустого списка с помощью LINQ

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

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(); 
} 
+5

Гораздо лучше не смешивать `is` и` cast`, а использовать `as` и` null` check: `ICollection collection = list as ICollection ; if (collection! = null) return colllection.Count; ` – abatishchev 2010-08-26 15:22:09

+2

Зачем писать дополнительный метод? Разве это не `list.Any()` эквивалентно `list.IsEmpty`? Рамочный метод должен быть оптимизирован - стоит написать новый, только если вы выяснили, что это узкое место. – dbkk 2011-06-18 17:43:51

+6

Кто-нибудь потрудился измерить производительность по их предлагаемым реализациям или все просто выбрасывают идеи? – 2013-03-01 15:26:52

ответ

93

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

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(), если никакая гарантия не может быть выполнена.

+4

Или используйте одну строку и верните (source == null)? true:! source.Any(); (Если вы не выбрасываете исключение) – Gage 2010-08-26 15:23:52

+1

Я бы сказал, да, выведите исключение для null, но затем добавьте второй метод расширения, называемый `IsNullOrEmpty()`. – devuxer 2011-12-09 20:52:59

+1

public static Boolean IsNullOrEmpty (этот IEnumerable источник) { 0 источник: источник == null || ! Source.Any(); } – dan 2012-05-25 04:38:36

6

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

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+ объекты в списке, и граф был еще быстрее, напротив с другими типами)

+10

`Enumerable.Count ()` имеет специальную обработку для `ICollection `. Если вы попробуете это с чем-то * другим *, чем с базовым списком, я ожидаю, что вы увидите * значительно * разные (более медленные) результаты. `Any()` останется примерно таким же. – 2009-10-26 05:08:08

+2

Я должен согласиться с Марком; это не очень честный тест. – 2010-08-26 15:08:45

+0

Любая идея, почему нет специальной обработки для `Enumerable.Any ()` for `ICollection `? конечно, без параметров `Any()` может просто проверить свойство `Count` для` ICollection `тоже? – Lukazoid 2012-01-23 14:04:52

2

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

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

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

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

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

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

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

+0

Различия между Any() и Count() кажутся ясными, но код профилирования @ тигля, по-видимому, указывает на то, что Count() работает быстрее для определенных реализаций IEnumerable . Для списка Я не могу получить Any(), чтобы получить более быстрый результат, чем Count(), пока размер списка не увеличится в тысячах элементов. LINQ сам по себе должен сделать некоторую серьезную оптимизацию вокруг метода Count(). – 2008-09-03 09:19:29

8

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

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

/EDIT @ Matt:

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

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

3

@ 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>.

-5

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

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

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

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

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

1

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

if(enumerable.FirstOrDefault() != null) 

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

3

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

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

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

14

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

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

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

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

var genericCollection = list as ICollection<T>; 

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

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

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; 
} 
Смежные вопросы