Jaki jest „najlepszy” (biorąc pod uwagę zarówno szybkość, jak i czytelność) sposób określenia, czy lista jest pusta? Nawet jeśli lista jest typu IEnumerable<T>
i nie ma właściwości Count.
W tej chwili rzucam się między to:
if (myList.Count() == 0) { ... }
i to:
if (!myList.Any()) { ... }
Domyślam się, że druga opcja jest szybsza, ponieważ wróci z wynikiem, gdy tylko zobaczy pierwszy element, podczas gdy druga opcja (dla IEnumerable) będzie musiała odwiedzić każdy element, aby zwrócić liczbę.
Biorąc to pod uwagę, czy druga opcja wygląda na tak czytelną? Które wolisz? A może możesz wymyślić lepszy sposób na przetestowanie pustej listy?
Odpowiedź Edit @ lassevk wydaje się najbardziej logiczna, w połączeniu z odrobiną sprawdzenia w czasie wykonywania, aby użyć liczby z pamięci podręcznej, jeśli to możliwe, na przykład:
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;
return !list.Any();
}
is
, acast
jednak stosowanieas
inull
sprawdzić:ICollection<T> collection = list as ICollection<T>; if (collection != null) return colllection.Count;
list.Any()
równoważnelist.IsEmpty
? Metoda frameworka powinna zostać zoptymalizowana - warto napisać nową tylko wtedy, gdy zorientowałeś się, że jest to wąskie gardło perf.IsEmpty
metodę rozszerzenia. github.com/dotnet/corefx/issues/35054 Sprawdź i zagłosuj, jeśli chcesz i zgadzasz się.Odpowiedzi:
Możesz to zrobić:
Edycja : Zwróć uwagę, że zwykłe użycie metody .Count będzie szybkie, jeśli bazowe źródło rzeczywiście ma szybką właściwość Count. Poprawną optymalizacją powyżej byłoby wykrycie kilku typów podstawowych i po prostu użycie ich właściwości .Count zamiast podejścia .Any (), ale następnie powrót do .Any (), jeśli nie można zagwarantować.
źródło
IsNullOrEmpty()
.return !source?.Any() ?? true;
Dodałbym jeden mały dodatek do kodu, na którym wydaje się, że się zdecydowałeś: sprawdź również
ICollection
, ponieważ jest to zaimplementowane nawet przez niektóre nieaktualne klasy ogólne (tj.Queue<T>
IStack<T>
). Używałbym równieżas
zamiast,is
ponieważ jest to bardziej idiomatyczne i okazało się, że jest szybsze .źródło
NotSupportedException
lubNotImplementedException
. Po raz pierwszy użyłem twojego przykładu kodu, kiedy dowiedziałem się, że kolekcja, której używałem, rzuciła wyjątek dla Count (kto wiedział ...).Czy to cię dziwi? Wyobrażam sobie, że dla
IList
implementacji,Count
po prostu odczytuje liczbę elementów bezpośrednio podczasAny
musi kwerendyIEnumerable.GetEnumerator
metody tworzenia instancji i połączeniaMoveNext
co najmniej raz./ EDYCJA @Matt:
Tak, oczywiście, że tak. To miałem na myśli. Właściwie używa
ICollection
zamiast,IList
ale wynik jest taki sam.źródło
Właśnie napisałem szybki test, spróbuj tego:
Drugi jest prawie trzykrotnie wolniejszy :)
Ponowna próba testu stopera ze stosem lub tablicą lub innymi scenariuszami naprawdę zależy od typu listy, którą się wydaje - ponieważ dowodzą, że Count jest wolniejszy.
Więc myślę, że to zależy od typu listy, której używasz!
(Aby zaznaczyć, umieściłem na liście ponad 2000 obiektów i liczenie było nadal szybsze, w przeciwieństwie do innych typów)
źródło
Enumerable.Count<T>()
ma specjalną obsługę dlaICollection<T>
. Jeśli spróbujesz tego z czymś innym niż podstawowa lista, spodziewam się, że zobaczysz znacznie inne (wolniejsze) wyniki.Any()
pozostanie jednak mniej więcej taki sam.Enumerable.Any<T>()
dlaICollection<T>
? z pewnością bez parametrówAny()
może po prostu sprawdzićCount
właściwośćICollection<T>
również?List.Count
jest O (1) zgodnie z dokumentacją Microsoft:http://msdn.microsoft.com/en-us/library/27b47ht3.aspx
więc po prostu użyj
List.Count == 0
go znacznie szybciej niż zapytanieDzieje się tak, ponieważ ma element członkowski danych o nazwie Count, który jest aktualizowany za każdym razem, gdy coś jest dodawane lub usuwane z listy, więc kiedy wywołujesz
List.Count
, nie musi iterować przez każdy element, aby go uzyskać, po prostu zwraca element członkowski danych.źródło
Druga opcja jest znacznie szybsza, jeśli masz wiele przedmiotów.
Any()
wraca po znalezieniu 1 przedmiotu.Count()
musi przeglądać całą listę.Załóżmy na przykład, że wyliczenie zawiera 1000 pozycji.
Any()
sprawdziłby pierwszy, a następnie zwrócił true.Count()
zwróciłby 1000 po przejściu całego wyliczenia.Jest to potencjalnie gorsze, jeśli używasz jednego z przesłonięć predykatu - funkcja Count () nadal musi sprawdzać każdą pojedynczą pozycję, nawet jeśli jest tylko jedno dopasowanie.
Przyzwyczaisz się do używania Any one - ma to sens i jest czytelne.
Jedno zastrzeżenie - jeśli masz List, a nie tylko IEnumerable, użyj właściwości Count tej listy.
źródło
@Konrad zaskakuje mnie to, że w moich testach przekazuję listę do metody, która akceptuje
IEnumerable<T>
, więc środowisko wykonawcze nie może jej zoptymalizować, wywołując metodę rozszerzenia Count () dlaIList<T>
.Mogę tylko założyć, że metoda rozszerzenia Count () dla IEnumerable robi coś takiego:
... innymi słowy, trochę optymalizacji czasu wykonywania dla specjalnego przypadku
IList<T>
./ EDYCJA @Konrad +1 mate - masz rację co do tego, że jest bardziej prawdopodobne, że jesteś włączony
ICollection<T>
.źródło
Ok, a co z tym?
EDYCJA: Właśnie zdałem sobie sprawę, że ktoś już naszkicował to rozwiązanie. Wspomniano, że zrobi to metoda Any (), ale dlaczego nie zrobić tego samemu? pozdrowienia
źródło
using
bloku, ponieważ w przeciwnym razie skonstruowałeśIDisposable
obiekt, a następnie porzuciłeś. Następnie, oczywiście, stanie się bardziej zwięzły, gdy użyjesz metody rozszerzenia, która już istnieje, i po prostu zmienisz ją nareturn !enumerable.Any()
(która robi dokładnie to).Any()
działa dokładnie tak, więc dodanie dokładnie tej samej metody z inną nazwą będzie po prostu mylące.Inny pomysł:
Jednak bardziej podoba mi się podejście Any ().
źródło
Było to krytyczne, aby to działało z Entity Framework:
źródło
Jeśli sprawdzam za pomocą Count () Linq wykonuje "SELECT COUNT (*) .." w bazie danych, ale muszę sprawdzić, czy wyniki zawierają dane, zdecydowałem się na wprowadzenie FirstOrDefault () zamiast Count ();
Przed
Po
źródło
źródło
Oto moja implementacja odpowiedzi Dana Tao, uwzględniająca predykat:
źródło
źródło
myList.ToList().Count == 0
. To wszystkoźródło
Ta metoda rozszerzenia działa dla mnie:
źródło