Domanda

Qual è il modo "migliore" (tenendo conto sia della velocità che della leggibilità) per determinare se un elenco è vuoto?Anche se l'elenco è di tipo IEnumerable<T> e non ha una proprietà Count.

In questo momento sono indeciso tra questo:

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

e questo:

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

La mia ipotesi è che la seconda opzione sia più veloce, poiché restituirà un risultato non appena vedrà il primo elemento, mentre la seconda opzione (per un IEnumerable) dovrà visitare ogni elemento per restituire il conteggio.

Detto questo, la seconda opzione ti sembra altrettanto leggibile?Quale preferiresti?Oppure riesci a pensare a un modo migliore per testare un elenco vuoto?

Modificare La risposta di @lassevk sembra essere la più logica, abbinata a un po' di controllo in fase di esecuzione per utilizzare un conteggio memorizzato nella cache, se possibile, in questo modo:

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

    return !list.Any();
}
È stato utile?

Soluzione

Potresti fare questo:

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

Modificare:Tieni presente che il semplice utilizzo del metodo .Count sarà veloce se l'origine sottostante dispone effettivamente di una proprietà Count veloce.Un'ottimizzazione valida di cui sopra sarebbe quella di rilevare alcuni tipi di base e utilizzare semplicemente la proprietà .Count di questi, invece dell'approccio .Any(), ma poi ricorrere a .Any() se non è possibile fornire alcuna garanzia.

Altri suggerimenti

Vorrei fare una piccola aggiunta al codice su cui sembra che tu abbia deciso:controlla anche ICollection, poiché questo è implementato anche da alcune classi generiche non obsolete (ad esempio, Queue<T> E Stack<T>).Utilizzerei anche as invece di is poiché è più idiomatico e ha dimostrato di essere più veloce.

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

Lo stesso LINQ deve in qualche modo eseguire una seria ottimizzazione del metodo Count().

Questo ti sorprende?Lo immagino per IList implementazioni, Count legge semplicemente direttamente il numero di elementi mentre Any deve interrogare il IEnumerable.GetEnumerator metodo, crea un'istanza e chiama MoveNext almeno una volta.

/EDIT @Matt:

Posso solo supporre che il metodo di estensione Count() per IEnumerable stia facendo qualcosa del genere:

Sì, certo che lo fa.Questo è ciò che intendevo.In realtà, usa ICollection invece di IList ma il risultato è lo stesso.

Ho appena scritto un test veloce, prova questo:

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

Il secondo è quasi tre volte più lento :)

Provare di nuovo il test del cronometro con uno Stack o un array o altri scenari dipende in realtà dal tipo di elenco che sembra, perché dimostrano che Count è più lento.

Quindi immagino che dipenda dal tipo di elenco che stai utilizzando!

(Giusto per sottolineare, ho inserito più di 2000 oggetti nell'elenco e il conteggio è stato ancora più veloce, al contrario di altri tipi)

List.Count è O(1) secondo la documentazione di Microsoft:
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

quindi basta usare List.Count == 0 è molto più veloce di una query

Questo perché ha un membro dati chiamato Count che viene aggiornato ogni volta che qualcosa viene aggiunto o rimosso dall'elenco, quindi quando chiami List.Count non è necessario scorrere ogni elemento per ottenerlo, restituisce semplicemente il membro dati.

La seconda opzione è molto più rapida se hai più articoli.

  • Any() ritorna non appena viene trovato 1 articolo.
  • Count() deve continuare a scorrere l'intero elenco.

Ad esempio, supponiamo che l'enumerazione contenga 1000 elementi.

  • Any() controllerà il primo, quindi restituirà true.
  • Count() restituirebbe 1000 dopo aver attraversato l'intera enumerazione.

Ciò è potenzialmente peggiore se si utilizza una delle sostituzioni del predicato: Count() deve comunque controllare ogni singolo elemento, anche se esiste una sola corrispondenza.

Ti abitui a usare Any one: ha senso ed è leggibile.

Un avvertimento: se hai un List, anziché solo un IEnumerable, usa la proprietà Count di quell'elenco.

@Konrad ciò che mi sorprende è che nei miei test sto passando l'elenco in un metodo che accetta IEnumerable<T>, quindi il runtime non può ottimizzarlo chiamando il metodo di estensione Count() per IList<T>.

Posso solo supporre che il metodo di estensione Count() per IEnumerable stia facendo qualcosa del genere:

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 altre parole, un po' di ottimizzazione del runtime per il caso speciale di IList<T>.

/EDIT @Konrad +1 amico, hai ragione sul fatto che è più probabile che sia attivo ICollection<T>.

Ok, allora che mi dici di questo?

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

MODIFICARE:Mi sono appena accorto che qualcuno ha già abbozzato questa soluzione.È stato detto che il metodo Any() farà questo, ma perché non farlo da solo?Saluti

Un'altra idea:

if(enumerable.FirstOrDefault() != null)

Tuttavia mi piace di più l'approccio Any().

Questo è stato fondamentale per farlo funzionare con Entity Framework:

var genericCollection = list as ICollection<T>;

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

Se controllo con Count() Linq esegue un "SELECT COUNT(*).." nel database, ma devo verificare se i risultati contengono dati, ho deciso di introdurre FirstOrDefault() invece di Count();

Prima

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

if (cfop.Count() > 0)
{
    var itemCfop = cfop.First();
    //....
}

Dopo

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

Ecco la mia implementazione della risposta di Dan Tao, consentendo un predicato:

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.È tutto

Questo metodo di estensione funziona per me:

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top