2012-03-09 11 views
12

Digamos que quiero verificar si hay al menos N elementos en una colección.Recuento de LINQ() hasta que, ¿es esto más eficiente?

¿Es esto mejor que hacer?

Count() >= N

Usando:

public static bool AtLeast<T>(this IEnumerable<T> enumerable, int max) 
    { 
     int count = 0; 
     return enumerable.Any(item => ++count >= max); 
    } 

O incluso

public static bool Equals<T>(this IEnumerable<T> enumerable, int amount) 
    { 
     return enumerable.Take(amount).Count() == amount; 
    } 

¿Cómo podría este punto de referencia?

/// <summary> 
    /// Returns whether the enumerable has at least the provided amount of elements. 
    /// </summary> 
    public static bool HasAtLeast<T>(this IEnumerable<T> enumerable, int amount) 
    { 
     return enumerable.Take(amount).Count() == amount; 
    } 

    /// <summary> 
    /// Returns whether the enumerable has at most the provided amount of elements. 
    /// </summary> 
    public static bool HasAtMost<T>(this IEnumerable<T> enumerable, int amount) 
    { 
     return enumerable.Take(amount + 1).Count() <= amount; 
    } 
+3

_Cómo iba a hacerlo esta referencia _ - Ponga el código que normalmente se utiliza para invocarlos en un bucle con el tiempo ..? –

+0

otra opción: enumerable.Select ((o, idx) => idx). Cualquier (i => i> = max); – KristoferA

Respuesta

5

Hay algunas optimizaciones bien documentadas integradas en el método .Count(). Específicamente, si su enumerable es ICollection, .Count() será una operación de tiempo constante ya que usará la propiedad ICollection de .Count.

Sin embargo, en el caso general iterará todo el IEnumerable para obtener el conteo. Si no tiene un ICollection, sería mejor que use cualquiera de sus dos métodos sugeridos cuando hay más de N elementos. Para el rendimiento relativo de esos dos, tendrías que perfilarlos como otros sugirieron.

+0

Sería cauteloso sobre sus implementaciones. ¿Se nos garantiza que cualquier método de Linq será seguro para hilos con la escritura en una variable externa como esa? Si atraviesa el 'IEnumerable ' secuencialmente, debería estar bien. Pero si en algún momento es posible tener múltiples hilos evaluando eso en paralelo, eso puede estar mal definido. –

+0

@Mike: como mínimo, creo que el enfoque '.Take (n)' estaría bien, después de todo, es simplemente iterar el enumerable y detenerse después de que se hayan alcanzado los elementos 'n' o que toda la colección haya sido iterada (si hay menos elementos 'n' en' IEnumerable'). No estoy seguro sobre la seguridad del hilo del enfoque '.Any()'. – goric

2
 var list = Enumerable.Range(1, 1000000); 
     var t = new Stopwatch(); 

     t.Restart(); 
     var count = list.Count() > 100000; 
     t.Stop(); 
     PrintTime(t, count); 

     t.Restart(); 
     var atLeast = list.AtLeast(100000); 
     t.Stop(); 
     PrintTime(t, atLeast); 

     t.Restart(); 
     var equals = list.Equalss(100000); 
     t.Stop(); 
     PrintTime(t, equals); 

Resultados donde imprimeHora() imprime los pasos de temporizador:

True 20818 
True 8774 
True 8688 
+1

esto ni siquiera tiene sentido, utilizando una lista que sabe con certeza que el 'Conteo 'es 100000, y no necesita invocar ningún método. ¿Cuáles son sus opciones de usar simplemente enumerable.range, sin la lista? – bevacqua

+0

Nico, buen punto :) Respuesta actualizada para reflejar resultados utilizando solo Enumerable.Range. – Beyers

+0

+1 para dar los tiempos reales. – jwg

Cuestiones relacionadas