Frage

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

Im Moment schwanke ich zwischen Folgendem:

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

und das:

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

Ich vermute, dass die zweite Option schneller ist, da sie ein Ergebnis liefert, sobald sie das erste Element sieht, während die zweite Option (für ein IEnumerable) jedes Element besuchen muss, um die Anzahl zurückzugeben.

Erscheint Ihnen die zweite Option dennoch gut lesbar?Was würdest du bevorzugen?Oder können Sie sich eine bessere Möglichkeit zum Testen auf eine leere Liste vorstellen?

Bearbeiten Die Antwort von @lassevk scheint die logischste zu sein, gepaart mit ein wenig Laufzeitprüfung, um nach Möglichkeit eine zwischengespeicherte Anzahl zu verwenden, etwa so:

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

    return !list.Any();
}
War es hilfreich?

Lösung

Sie könnten Folgendes 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 einfache Verwendung der .Count-Methode schnell ist, wenn die zugrunde liegende Quelle tatsächlich über eine schnelle Count-Eigenschaft verfügt.Eine gültige Optimierung oben wäre, einige Basistypen zu erkennen und einfach deren .Count-Eigenschaft anstelle des .Any()-Ansatzes zu verwenden, dann aber auf .Any() zurückzugreifen, wenn keine Garantie gegeben werden kann.

Andere Tipps

Ich würde eine kleine Ergänzung zu dem Code vornehmen, für den Sie sich offenbar entschieden haben:Überprüfen Sie auch, ob ICollection, da dies auch von einigen nicht veralteten generischen Klassen implementiert wird (z. B. Queue<T> Und Stack<T>).Würde ich auch nutzen as anstatt is da es idiomatischer ist und hat sich als schneller erwiesen.

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 selbst muss irgendwie ernsthafte Optimierungen an der Count()-Methode vornehmen.

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

/EDIT @Matt:

Ich kann nur davon ausgehen, dass die Count()-Erweiterungsmethode für IEnumerable etwa Folgendes tut:

Ja, natürlich tut es das.Das habe ich gemeint.Eigentlich nutzt es ICollection anstatt IList aber das Ergebnis ist das gleiche.

Ich habe gerade einen kurzen Test geschrieben, versuchen Sie Folgendes:

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

Der zweite ist fast dreimal langsamer :)

Wenn Sie den Stoppuhrtest erneut mit einem Stack, einem Array oder anderen Szenarien versuchen, kommt es wirklich auf die Art der Liste an, die es zu sein scheint – denn sie erweisen, dass Count langsamer ist.

Ich vermute also, dass es von der Art der Liste abhängt, die Sie verwenden!

(Nur um darauf hinzuweisen, ich habe mehr als 2000 Objekte in die Liste eingefügt und die Zählung war immer noch schneller, im Gegensatz zu anderen Typen.)

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

also einfach nutzen List.Count == 0 es ist viel schneller als eine Abfrage

Dies liegt daran, dass es ein Datenelement namens Count gibt, das jedes Mal aktualisiert wird, wenn etwas zur Liste hinzugefügt oder daraus entfernt wird, also wenn Sie aufrufen List.Count Es muss nicht jedes Element durchlaufen, um es zu erhalten, es gibt lediglich das Datenelement zurück.

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

  • Any() kehrt zurück, sobald 1 Artikel gefunden wurde.
  • Count() muss immer wieder die gesamte Liste durchgehen.

Angenommen, die Aufzählung umfasst 1000 Elemente.

  • Any() würde das erste überprüfen und dann true zurückgeben.
  • Count() würde 1000 zurückgeben, nachdem die gesamte Aufzählung durchlaufen wurde.

Dies ist möglicherweise noch schlimmer, wenn Sie eine der Prädikatüberschreibungen verwenden – Count() muss immer noch jedes einzelne Element überprüfen, selbst wenn es nur eine Übereinstimmung gibt.

Man gewöhnt sich daran, Any zu verwenden – es macht Sinn und ist lesbar.

Eine Einschränkung: Wenn Sie eine Liste und nicht nur eine IEnumerable haben, verwenden Sie die Count-Eigenschaft dieser Liste.

@Konrad, was mich überrascht, ist, dass ich in meinen Tests die Liste an eine Methode übergebe, die akzeptiert IEnumerable<T>, daher kann die Laufzeit es nicht durch Aufrufen der Count()-Erweiterungsmethode für optimieren IList<T>.

Ich kann nur davon ausgehen, dass die Count()-Erweiterungsmethode für IEnumerable etwa Folgendes tut:

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

...mit anderen Worten, ein bisschen Laufzeitoptimierung für den Sonderfall von IList<T>.

/EDIT @Konrad +1 Kumpel – du hast Recht damit, dass es wahrscheinlicher ist, dass es läuft ICollection<T>.

Ok, was ist also mit diesem hier?

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

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

Eine andere Idee:

if(enumerable.FirstOrDefault() != null)

Allerdings gefällt mir der Any()-Ansatz besser.

Dies war entscheidend, damit dies mit Entity Framework funktioniert:

var genericCollection = list as ICollection<T>;

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

Wenn ich mit Count() nachprüfe, führt Linq ein „SELECT COUNT(*)“ in der Datenbank aus, ich muss aber prüfen, ob die Ergebnisse Daten enthalten. Ich habe beschlossen, FirstOrDefault() anstelle von Count(); einzuführen.

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)
{
    //....
}
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);
    }

Hier ist meine Implementierung von Dan Taos Antwort unter Berücksichtigung eines Prädikats:

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.Das ist alles

Diese Erweiterungsmethode funktioniert für mich:

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top