2008-09-03 11 views
121

Was ist die "beste" (unter Berücksichtigung von Geschwindigkeit und Lesbarkeit), um festzustellen, ob eine Liste leer ist? Auch wenn die Liste vom Typ IEnumerable<T> ist und keine Count-Eigenschaft hat.Überprüfen, ob eine Liste mit LINQ leer ist

Im Moment bin ich warf zwischen auf den Punkt:

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

und dieses:

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

Meine Vermutung ist, dass die zweite Option ist schneller, da es mit einem Ergebnis zurückkommen werde sobald es das erste Element sieht, während die zweite Option (für ein IEnumerable) jedes Element besuchen muss, um die Anzahl zurückzugeben.

Das gesagt, sieht die zweite Option für Sie lesbar aus? Was würdest du bevorzugen? Oder können Sie sich einen besseren Weg vorstellen, um nach einer leeren Liste zu suchen?

bearbeiten @ Antwort lassevk das scheint die logischste zu sein, gepaart mit ein bisschen Laufzeit eine im Cache gespeicherte Zählung, wenn möglich, wie diese zu verwenden Überprüfung:

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

    return !list.Any(); 
} 
+5

Viel besser nicht mischen 'is' und' cast' aber verwenden 'as' und' null' check: 'ICollection collection = Liste als ICollection ; if (collection! = null) return colllection.Count; ' – abatishchev

+2

Warum eine zusätzliche Methode schreiben? Ist nicht 'list.Any()' äquivalent zu 'list.IsEmpty'? Die Framework-Methode sollte optimiert werden - es lohnt sich, nur dann eine neue zu schreiben, wenn Sie herausgefunden haben, dass dies ein perfekter Flaschenhals ist. – dbkk

+6

Hat sich jemand Mühe gegeben, die Leistung seiner vorgeschlagenen Implementierungen zu messen, oder werfen alle nur Ideen aus? –

Antwort

93

Sie können dies tun:

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

Bearbeiten: Beachten Sie, dass die Verwendung der .Count-Methode schnell ist, wenn die zugrunde liegende Quelle tatsächlich eine schnelle Count-Eigenschaft hat. Eine gültige Optimierung oben wäre, einige Basistypen zu erkennen und einfach die .Count-Eigenschaft dieser anstelle der .Any() - Methode zu verwenden, aber dann auf .Any() zurückzugreifen, wenn keine Garantie gegeben werden kann.

+4

Oder eine Zeile verwenden und zurückgeben (source == null)? wahr:! source.Any(); (Wenn Sie keine Ausnahme auslösen) – Gage

+1

Ich würde sagen, ja, eine Ausnahme für null werfen, aber fügen Sie dann eine zweite Erweiterungsmethode namens 'IsNullOrEmpty()'. – devuxer

+1

public static Boolean IsNullOrEmpty (diese IEnumerable Quelle) { Rückgabequell == null || ! source.Any(); } – dan

6

Ich schrieb einen kurzen Test, versuchen Sie dies:

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

Die zweite fast dreimal :) langsamer ist

Der Versuch, den Stoppuhr-Tests wieder mit einem Stapel oder Array oder anderen Szenarien es wirklich hängt von der Art der Liste ab - weil sie beweisen, dass Count langsamer ist.

Also ich denke, es hängt von der Art der Liste, die Sie verwenden!

(Just weisen darauf hin, habe ich 2000+ Objekte in der Liste und Graf war noch schneller, gegenüber mit anderen Typen)

+10

'Enumerable.Count ()' hat spezielle Behandlung für 'ICollection '. Wenn Sie dies mit etwas * anderem * als einer Basisliste versuchen, erwarte ich, dass Sie * signifikant * andere (langsamere) Ergebnisse sehen werden. 'Any()' bleibt jedoch ungefähr gleich. –

+2

Ich muss mit Marc übereinstimmen; Das ist kein wirklich fairer Test. –

+0

Irgendeine Idee, warum es keine spezielle Behandlung für 'Enumerable.Any ()' für 'ICollection ' gibt? sicherlich könnte das parameterlose 'Any()' einfach die 'Count'-Eigenschaft für 'ICollection ' auch überprüfen? – Lukazoid

2

Die zweite Option ist viel schneller, wenn Sie mehrere Elemente haben.

  • Any() zurück, sobald 1 Artikel gefunden wurde.
  • Count() muss weiter durch die gesamte Liste gehen.

Angenommen, die Aufzählung hatte 1000 Elemente.

  • Any() würde die erste überprüfen, dann true zurück.
  • Count() würde nach dem Durchlaufen der gesamten Aufzählung 1000 zurückgeben.
  • Dies ist möglicherweise schlimmer, wenn Sie eine der Prädikatenüberschreibungen verwenden - Count() muss noch jedes einzelne Element überprüfen, auch wenn es nur eine Übereinstimmung gibt.

    Sie gewöhnen sich an die Any - es macht Sinn und ist lesbar.

    Ein Vorbehalt - wenn Sie eine Liste und nicht nur einen IEnumerable haben, verwenden Sie die Count-Eigenschaft dieser Liste.

    +0

    Die Unterschiede zwischen Any() und Count() scheinen klar zu sein, aber der Profilierungscode von @ tiegel scheint darauf hinzuweisen, dass Count() für bestimmte Implementierungen von IEnumerable schneller ist. Für Liste kann ich Any() nicht erhalten, um ein schnelleres Ergebnis als Count() zu geben, bis die Listengröße in den Tausenden von Elementen oben ist. LINQ selbst muss die Art von Count() irgendwie ernsthaft optimieren. –

    8

    LINQ selbst muss einige ernsthafte Optimierung um die Count() -Methode irgendwie tun.

    Überrascht Sie das? Ich stelle mir vor, dass für IList Implementierungen Count einfach die Anzahl der Elemente direkt liest, während Any die IEnumerable.GetEnumerator Methode abfragen, eine Instanz erstellen und MoveNext mindestens einmal aufrufen muss.

    /EDIT @ Matt:

    Ich kann nur annehmen, dass die Count() Erweiterungsmethode für IEnumerable ist so etwas wie dies zu tun:

    Ja, natürlich tut es. Das habe ich gemeint. Tatsächlich verwendet es ICollection anstelle von IList, aber das Ergebnis ist das gleiche.

    3

    @Konrad was mich verblüfft ist, dass ich in meinen Tests die Liste in eine Methode übergibt, die IEnumerable<T> akzeptiert, so kann die Laufzeit es nicht optimieren, indem die Count() - Erweiterungsmethode für IList<T> aufruft.

    Ich kann nur annehmen, dass die Count() Erweiterungsmethode für IEnumerable ist so etwas wie dies zu tun:

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

    ... in anderen Worten, ein wenig Laufzeitoptimierung für den Sonderfall IList<T>.

    /BEARBEITEN @Konrad +1 Kumpel - Sie haben recht damit wahrscheinlich eher auf ICollection<T>.

    -5

    Diese Erweiterung Methode funktioniert für mich:

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

    Vermeiden Sie die Verwendung von Ausnahmen. Im obigen Code * erwarten * Sie eine Ausnahme für bestimmte, gut definierte Eingaben (d. H. Leere Aufzählungen). Daher sind sie keine Ausnahmen, sie sind die Regel. Das ist ein Missbrauch dieses Kontrollmechanismus, der Auswirkungen auf die Lesbarkeit und Leistung hat. Reservieren Sie die Verwendung von Ausnahmen für wirklich außergewöhnliche Fälle. –

    +0

    Generell würde ich zustimmen. Dies ist jedoch eine Problemumgehung für eine entsprechende fehlende IsEmpty-Methode. Und ich würde argumentieren, dass ein Workaround nie der ideale Weg ist, um etwas zu tun ... Darüber hinaus, besonders in diesem Fall, ist die Absicht sehr klar und der "schmutzige" Code wird eingekapselt und an einem genau definierten Ort versteckt. –

    +3

    -1: Wenn Sie dies so machen wollen, verwenden Sie FirstOrDefault(), wie in ChulioMartinez's Antwort. –

    1

    Ok, so was ist dieses?

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

    EDIT: Ich habe gerade festgestellt, dass jemand diese Lösung bereits skizziert hat. Es wurde erwähnt, dass die Any() Methode dies tun wird, aber warum nicht selbst? Grüße

    +3

    ABER es wird weniger prägnant, wenn Sie es richtig in einen 'using'-Block einschließen, da Sie andernfalls ein' IDisposable'-Objekt konstruiert und dann aufgegeben haben. Dann wird es natürlich * prägnanter, wenn Sie die bereits vorhandene Erweiterungsmethode verwenden und sie einfach in "return! Enumerable.Any()" ändern (was genau das tut). –

    +0

    Warum eine bereits existierende Methode umschreiben? Wie bereits erwähnt, führt 'Any()' genau das aus, daher ist es einfach verwirrend, genau dieselbe Methode mit einem anderen Namen hinzuzufügen. –

    1

    Eine andere Idee:

    if(enumerable.FirstOrDefault() != null) 
    

    aber ich mag die Any() Ansatz.

    +1

    Was ist, wenn Sie eine nicht leere Liste haben, in der das erste Element null ist? – Ekevoo

    3

    List.Count ist O (1) nach der Dokumentation von Microsoft:
    http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

    so nur List.Count == 0 verwendet es ist viel schneller als eine Abfrage

    Dies ist, weil es ein Datenelement namens Count hat, die jede aktualisiert Zeit, die etwas hinzugefügt oder aus der Liste entfernt wird, wenn Sie also List.Count aufrufen, muss es nicht jedes Element durchlaufen, um es zu erhalten, es gibt nur das Datenelement zurück.

    +1

    Wenn es ein "IEnumerable" ist, dann nein. (Für Starter hat IEnumerable keine "Count" -Eigenschaft, es hat eine Count() -Methode.). Der Aufruf von "Count()" erfordert, dass IEnumerable jedes einzelne Element in der Liste überprüft. Wohingegen "Any" einfach zurückkommt, sobald es 1 Element findet. – 00jt

    +0

    Es hängt von der Datenquelle ab. Wenn Sie die Ausbeute verwenden, um ein IEnumerable zu erstellen, muss es das IEnumerable durchqueren, um seine Größe zu kennen. So ist es in einigen Fällen nur O (1). Es ist nicht immer O (1). – TamusJRoyce

    14

    Ich würde eine kleine Ergänzung zu dem Code scheinen Sie auf niedergelassen haben: überprüfen Sie auch für ICollection, da dies sogar auch von einigen nicht veralteten generischen Klassen implementiert wird (das heißt, Queue<T> und Stack<T>). Ich würde auch as anstelle von is verwenden, da es idiomatischer ist und 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(); 
    } 
    
    +1

    Ich mag diese Antwort. Ein Warnhinweis ist, dass einige Auflistungen Ausnahmen auslösen, wenn sie eine Schnittstelle wie "NotSupportedException" oder "NotImplementedException" nicht vollständig implementieren. Ich habe zuerst Ihr Codebeispiel verwendet, als ich herausgefunden habe, dass eine Sammlung, die ich verwendet habe, eine Ausnahme für Count geworfen hat (wer wusste ...). – Sam

    0

    Wenn ich mit dem Grafen überprüfen() Linq eine „SELECT COUNT (*) ..“ führt in der Datenbank, aber ich muss überprüfen, ob die Ergebnisdaten enthält, entschloß ich mich auf die Einführung FirstOrDefault() anstelle von Anzahl();

    Vor

    var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>() 
    
    if (cfop.Count() > 0) 
    { 
        var itemCfop = cfop.First(); 
        //.... 
    } 
    

    Nach

    var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>() 
    
    var itemCfop = cfop.FirstOrDefault(); 
    
    if (itemCfop != null) 
    { 
        //.... 
    } 
    
    1

    Dies war entscheidend für dieses Profil mit Entity Framework zu arbeiten:

    var genericCollection = list as ICollection<T>; 
    
    if (genericCollection != null) 
    { 
        //your code 
    } 
    
    +0

    Wie beantwortet das die Frage? Die Sammlung kann nicht null sein, während keine Elemente darin enthalten sind. –

    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; 
    
    +1

    Zumindest erklären, was dieser Code tut. – quantum

    0

    Hier ist meine Implementierung von Dan Tao Antwort, so dass für ein Prädikat:

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

    myList.ToList().Count == 0. Das ist alles

    +1

    Das ist eine schreckliche Idee. ToList() sollte nicht überstrapaziert werden, da das Enumerable vollständig ausgewertet werden muss. Verwenden Sie stattdessen .Any(). –