objects from examples consultas c# linq collections

c# - from - Linq.Any VS.Exists-¿Cuál es la diferencia?



select linq c# list (6)

Al usar Linq en las colecciones, ¿cuál es la diferencia entre las siguientes líneas de código?

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

y

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

Actualización 1

Cuando .Exists , parece que no hay código.

Actualización 2

¿Alguien sabe por qué no hay código para este?


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


Como continuación de la respuesta de Matas sobre el benchmarking.

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

En primer lugar, la evaluación comparativa con el cronómetro no es precisa ( consulte la respuesta de series0ne sobre un tema diferente, pero similar ), 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 entre sí el rendimiento de los dos métodos es mediante la ejecución de ambos métodos muchas veces y luego comparando el tiempo de ejecución más rápido de cada uno. De esa manera, realmente no importa que JITing y otros ruidos nos den malas lecturas (y lo hacen ), 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 == "0123456789012")) { sw.Stop(); existsTimes.Add(sw.ElapsedTicks); } } for (int j = 0; j < 1000; j++) { sw2 = Stopwatch.StartNew(); if (!list.Exists(o => o == "0123456789012")) { 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 bastante rápidos.

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

Hay una pequeña diferencia, pero es una diferencia demasiado pequeña para no ser explicada por el ruido de fondo. Supongo que si uno hiciera 10 000 o 100 000 Exists() y Any() lugar, esa pequeña diferencia desaparecería más o menos.


Cuando corrija las mediciones, como se mencionó anteriormente: Cualquiera y existe, y agregando el promedio, obtendremos la siguiente salida:

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.


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

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

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

  • Cualquiera también tiene una sobrecarga que no toma parámetros y simplemente busca cualquier elemento en el enumerable.
  • Exists no tiene tal sobrecarga.

Ver documentacion

List.Exists (Método de objeto - MSDN)

Determina si la Lista (T) contiene elementos que coinciden con las condiciones definidas por el predicado especificado.

Esto existe desde .NET 2.0, así que antes de LINQ. Pretende ser usado con el delegado del Predicado, pero las expresiones lambda son compatibles con versiones anteriores. Además, solo List tiene esto (ni siquiera IList)

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

Determina si algún 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 fue pensado para ser usado con expresiones lambda y LINQ.

En comportamiento, estos son idénticos.


TLDR; En cuanto al rendimiento, Any parece ser más lento (si lo he configurado correctamente para evaluar ambos valores casi al mismo tiempo)

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

generador de lista de pruebas:

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 10M registros

"Cualquiera: 00: 00: 00.3770377 Existe: 00: 00: 00.2490249"

Con 5M registros

"Cualquiera: 00: 00: 00.0940094 Existe: 00: 00: 00.1420142"

Con 1M registros

"Cualquiera: 00: 00: 00.0180018 Existe: 00: 00: 00.0090009"

Con 500k (también cambié el orden en el que se evalúan para ver si no hay una operación adicional asociada con la que se ejecuta primero).

"Existe: 00: 00: 00.0050005 Cualquiera: 00: 00: 00.0100010"

Con 100k registros

"Existe: 00: 00: 00.0010001 Cualquiera: 00: 00: 00.0020002"

Parecería que Any fuera más lento por magnitud de 2.

Edición: Para los registros de 5 y 10 M, cambié la forma en que genera la lista y Exists repente se volvió más lento que Any que implica que hay algo mal 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 == "0123456789012"); if (forceListEval != "sdsdf") { var s = string.Empty; var start1 = DateTime.Now; if (!list1.Any(o => o == "0123456789012")) { var end1 = DateTime.Now; s += " Any: " + end1.Subtract(start1); } var start2 = DateTime.Now; if (!list1.Exists(o => o == "0123456789012")) { var end2 = DateTime.Now; s += " Exists: " + end2.Subtract(start2); } if (!s.Contains("sdfsd")) { } }

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

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

10M

"Cualquiera: 00: 00: 00.1640164 Existe: 00: 00: 00.0750075"

5M

"Cualquiera: 00: 00: 00.0810081 Existe: 00: 00: 00.0360036"

1M

"Cualquiera: 00: 00: 00.0190019 Existe: 00: 00: 00.0070007"

500k

"Cualquiera: 00: 00: 00.0120012 Existe: 00: 00: 00.0040004"