2009-05-18 10 views
320

Usando Linq en colecciones, ¿cuál es la diferencia entre las siguientes líneas de código?Linq. Cualquier VS. Exists - ¿Cuál es la diferencia?

if(!coll.Any(i => i.Value)) 

y

if(!coll.Exists(i => i.Value)) 

Actualización 1

Cuando desmonte .Exists Parece que no hay ningún código.

Actualización 2

Alguien sabe por qué no hay código de allí para éste?

+9

miradas ¿Cómo funciona el código como se ha compilado? ¿Cómo desmontaste? ildasm? ¿Qué esperabas encontrar pero no lo hiciste? – Meinersbur

Respuesta

335

Sede (método Object - MSDN)

List.Exists

determina si la lista (T) contiene elementos que coincidan con las condiciones definidas por el predicado especificado.

Esto existe desde .NET 2.0, entonces antes de LINQ. Significa que se utilizará con el predicado delegado, pero las expresiones lambda son compatibles con versiones anteriores. Además, sólo tiene esta lista (ni siquiera IList)

IEnumerable.Any (método de extensión - MSDN)

Determina si cualquier elemento de una secuencia satisface una condición.

Esto es nuevo en .NET 3.5 y usa Func (TSource, bool) como argumento, por lo que este fue diseñado para ser utilizado con expresiones lambda y LINQ.

En el comportamiento, estos son idénticos.

+1

Más tarde hice [una publicación en otro hilo] (http: // stackoverflow.com/a/21436464/1336654) donde enumeré todos los "equivalentes" de Linq de los métodos de instancia de .NET 2 'List <>'. –

169

La diferencia es que Any es un método de extensión para cualquier IEnumerable<T> definido en System.Linq.Enumerable. Se puede usar en cualquier instancia de IEnumerable<T>.

Exists no parece ser un método de extensión. Mi suposición es que coll es del tipo List<T>. Si es así, Exists es un método de instancia que funciona de manera muy similar a Any.

En resumen, los métodos son esencialmente los mismos. Uno es más general que el otro.

  • Cualquier también tiene una sobrecarga, que no toma parámetros y simplemente busca cualquier elemento de la enumerable.
  • Existe no tiene tal sobrecarga.
+10

Bien puesto (+1). List .Exists ha estado presente desde .Net 2 pero solo funciona para listas genéricas. IEnumerable . Todo se agregó en .Net 3 como una extensión que funciona en cualquier colección enumerable. También hay miembros similares como List .Count, que es una propiedad e IEnumerable .Count() - un método. – Keith

4

Además, esto solo funcionará si Value es de tipo bool. Normalmente esto se usa con predicados. Cualquier predicado generalmente se usaría para encontrar si hay algún elemento que satisfaga una condición dada. Aquí solo estás haciendo un mapa de tu elemento i a una propiedad bool. Buscará una "i" cuya propiedad Value sea verdadera. Una vez hecho, el método volverá verdadero.

38

TLDR;En cuanto al rendimiento Any parece ser más lenta (si ha configurado adecuadamente para evaluar tanto los valores en casi simultáneamente) lista

 var list1 = Generate(1000000); 
     var forceListEval = list1.SingleOrDefault(o => o == ""); 
     if (forceListEval != "sdsdf") 
     { 
      var s = string.Empty; 
      var start2 = DateTime.Now; 
      if (!list1.Exists(o => o == "")) 
      { 
       var end2 = DateTime.Now; 
       s += " Exists: " + end2.Subtract(start2); 
      } 

      var start1 = DateTime.Now; 
      if (!list1.Any(o => o == "")) 
      { 
       var end1 = DateTime.Now; 
       s +=" Any: " +end1.Subtract(start1); 
      } 

      if (!s.Contains("sdfsd")) 
      { 

      } 

prueba del generador:

private List<string> Generate(int count) 
    { 
     var list = new List<string>(); 
     for (int i = 0; i < count; i++) 
     { 
      list.Add(new string(
      Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13) 
       .Select(s => 
       { 
        var cryptoResult = new byte[4]; 
        new RNGCryptoServiceProvider().GetBytes(cryptoResult); 
        return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)]; 
       }) 
       .ToArray())); 
     } 

     return list; 
    } 

Con registros 10M

"Any: 00: 00: 00,3770377 existe: 00: 00: 00,2490249"

Con 5M registra

"Any: 00: 00: 00,0940094 existe: 00: 00: 00,1420142"

Con registros 1M

"Any: 00:00 : 00.0180018 Existe: 00: 00: 00.0090009 "

Con 500k, (también volteé orden en el que se evalúan para ver si no hay ninguna operación adicional asociado con lo que se ejecuta en primer lugar)

"existe: 00: 00: 00,0050005 Cualquier: 00: 00: 00,0100010".

Con 100k registros

"existe: 00: 00: 00,0010001 Cualquier: 00: 00: 00,0020002"

parecería Any t O sea más lento por la magnitud de 2.

Editar: Para 5 y registros 10M He cambiado la forma en que se genera la lista y Exists de repente se hizo más lento que Any lo que implica que hay algo malo en la forma en que estoy probando.

nuevo mecanismo de prueba:

private static IEnumerable<string> Generate(int count) 
    { 
     var cripto = new RNGCryptoServiceProvider(); 
     Func<string> getString =() => new string(
      Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13) 
       .Select(s => 
       { 
        var cryptoResult = new byte[4]; 
        cripto.GetBytes(cryptoResult); 
        return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)]; 
       }) 
       .ToArray()); 

     var list = new ConcurrentBag<string>(); 
     var x = Parallel.For(0, count, o => list.Add(getString())); 
     return list; 
    } 

    private static void Test() 
    { 
     var list = Generate(10000000); 
     var list1 = list.ToList(); 
     var forceListEval = list1.SingleOrDefault(o => o == ""); 
     if (forceListEval != "sdsdf") 
     { 
      var s = string.Empty; 

      var start1 = DateTime.Now; 
      if (!list1.Any(o => o == "")) 
      { 
       var end1 = DateTime.Now; 
       s += " Any: " + end1.Subtract(start1); 
      } 

      var start2 = DateTime.Now; 
      if (!list1.Exists(o => o == "")) 
      { 
       var end2 = DateTime.Now; 
       s += " Exists: " + end2.Subtract(start2); 
      } 

      if (!s.Contains("sdfsd")) 
      { 

      } 
     } 

Edit2: Ok por lo que para eliminar cualquier influencia de la generación de datos de prueba que lo escribí todo en archivo y ahora leer desde allí.

private static void Test() 
    { 
     var list1 = File.ReadAllLines("test.txt").Take(500000).ToList(); 
     var forceListEval = list1.SingleOrDefault(o => o == ""); 
     if (forceListEval != "sdsdf") 
     { 
      var s = string.Empty; 
      var start1 = DateTime.Now; 
      if (!list1.Any(o => o == "")) 
      { 
       var end1 = DateTime.Now; 
       s += " Any: " + end1.Subtract(start1); 
      } 

      var start2 = DateTime.Now; 
      if (!list1.Exists(o => o == "")) 
      { 
       var end2 = DateTime.Now; 
       s += " Exists: " + end2.Subtract(start2); 
      } 

      if (!s.Contains("sdfsd")) 
      { 
      } 
     } 
    } 

10M

"Any: 00: 00: 00,1640164 existe: 00:00:00.0750075"

5M

"Any: 00: 00: 00,0810081 existe: 00: 00: 00,0360036"

1M

"Any: 00:00 : 00.0190019 Existe: 00: 00: 00.0070007 "

500k

"Any: 00: 00: 00,0120012 existe: 00: 00: 00,0040004"

enter image description here

+1

No desacreditó a usted, pero me siento escéptico sobre estos puntos de referencia. Mira los números: cada resultado tiene una recurrencia (3770377: 2490249). Al menos para mí, esa es una señal segura de que algo no es correcto. No estoy cien por ciento seguro sobre las matemáticas aquí, pero creo que las probabilidades de que ocurra ese patrón recurrente es de 1 en 999^999 (o 999! ¿Quizás?) Por valor. Entonces, la posibilidad de que suceda ** 8 veces ** seguidas es infinitesimal. Creo que es porque utilizas [DateTime para benchmarking] (http://stackoverflow.com/questions/28637/is-datetime-now-the-best-way-to-measure-a-functions-performance). –

+0

@JerriKangasniemi Repetir la misma operación en aislamiento siempre debe tomar la misma cantidad de tiempo, lo mismo ocurre con repetirlo varias veces. ¿Qué te hace decir que es DateTime? –

+0

Por supuesto que sí. El problema sigue siendo que es muy poco probable que tome, por ejemplo, 0120012 segundos para llamadas de 500k. Y si fuera perfectamente lineal, lo que explicaría los números tan bien, las llamadas de 1M hubieran tomado 0240024 segundos (el doble de tiempo), sin embargo, ese no es el caso. Las llamadas de 1M requieren 58, (3)% más de 500k y 10M demoran 102,5% más de 5M. Por lo tanto, no es una función lineal y, por lo tanto, no es realmente razonable para todos los números recurrentes. Mencioné DateTime porque he tenido problemas con el mismo en el pasado, debido a que DateTime no usa temporizadores de alta precisión. –

6

Como continuación de Matas' answer en la evaluación comparativa.

TL/DR: Exists() y Any() son igualmente rápidos.

En primer lugar: La evaluación comparativa con Cronómetro no es precisa (see series0ne's answer on a different, but similiar, topic), pero es mucho más precisa que DateTime.

La forma de obtener lecturas realmente precisas es mediante el uso de perfiles de rendimiento. Pero una forma de tener una idea de cómo se comparan el rendimiento de los dos métodos es ejecutando ambos métodos cargas veces y luego comparando el tiempo de ejecución más rápido de cada uno. De esta forma, realmente no importa que JITing y otros ruidos nos den lecturas erróneas (y hace), porque ambas ejecuciones son "igualmente erróneas" en cierto sentido.

static void Main(string[] args) 
    { 
     Console.WriteLine("Generating list..."); 
     List<string> list = GenerateTestList(1000000); 
     var s = string.Empty; 

     Stopwatch sw; 
     Stopwatch sw2; 
     List<long> existsTimes = new List<long>(); 
     List<long> anyTimes = new List<long>(); 

     Console.WriteLine("Executing..."); 
     for (int j = 0; j < 1000; j++) 
     { 
      sw = Stopwatch.StartNew(); 
      if (!list.Exists(o => o == "")) 
      { 
       sw.Stop(); 
       existsTimes.Add(sw.ElapsedTicks); 
      } 
     } 

     for (int j = 0; j < 1000; j++) 
     { 
      sw2 = Stopwatch.StartNew(); 
      if (!list.Exists(o => o == "")) 
      { 
       sw2.Stop(); 
       anyTimes.Add(sw2.ElapsedTicks); 
      } 
     } 

     long existsFastest = existsTimes.Min(); 
     long anyFastest = anyTimes.Min(); 

     Console.WriteLine(string.Format("Fastest Exists() execution: {0} ticks\nFastest Any() execution: {1} ticks", existsFastest.ToString(), anyFastest.ToString())); 
     Console.WriteLine("Benchmark finished. Press any key."); 
     Console.ReadKey(); 
    } 

    public static List<string> GenerateTestList(int count) 
    { 
     var list = new List<string>(); 
     for (int i = 0; i < count; i++) 
     { 
      Random r = new Random(); 
      int it = r.Next(0, 100); 
      list.Add(new string('s', it)); 
     } 
     return list; 
    } 

Después de ejecutar el código anterior 4 veces (que a su vez hacen 1 000 Exists() y Any() en una lista con 1 000 000 elementos), no es difícil ver que los métodos son más o menos igual de rápido.

Fastest Exists() execution: 57881 ticks 
Fastest Any() execution: 58272 ticks 

Fastest Exists() execution: 58133 ticks 
Fastest Any() execution: 58063 ticks 

Fastest Exists() execution: 58482 ticks 
Fastest Any() execution: 58982 ticks 

Fastest Exists() execution: 57121 ticks 
Fastest Any() execution: 57317 ticks 

No es una ligera diferencia, pero es una diferencia demasiado pequeña como para no ser explicado por el ruido de fondo. Mi suposición sería que si uno hiciera 10 000 o 100 000 Exists() y Any(), esa pequeña diferencia desaparecería más o menos.

+0

¿Podría sugerirle que haga 10 000 y 100 000 y 1000000, solo para ser metódico al respecto, también por qué el valor mínimo y no el promedio? –

+2

El valor mínimo se debe a que quiero comparar la ejecución más rápida (= probablemente la menor cantidad de ruido de fondo) de cada método. Podría hacerlo con más iteraciones, aunque será más tarde (dudo que mi jefe me quiera pagar por hacer esto en lugar de trabajar a través de nuestro retraso) –

+0

Le he preguntado a Paul Lindberg y él dice que está bien;) en lo que respecta al mínimo I puede ver su razonamiento; sin embargo, un enfoque más ortodoxo es utilizar https://en.wikipedia.org/wiki/Algorithmic_efficiency#Practice –

5

Al corregir las mediciones - como se mencionó anteriormente: Todos y cada uno existe, y la adición de la media - nos pondremos siguiente resultado:

Executing search Exists() 1000 times ... 
Average Exists(): 35566,023 
Fastest Exists() execution: 32226 

Executing search Any() 1000 times ... 
Average Any(): 58852,435 
Fastest Any() execution: 52269 ticks 

Benchmark finished. Press any key. 
Cuestiones relacionadas