Find () vs. Where (). FirstOrDefault ()

161

A menudo veo personas que usan Where.FirstOrDefault()para hacer una búsqueda y agarrar el primer elemento. ¿Por qué no solo usar Find()? ¿Hay alguna ventaja para el otro? No pude notar la diferencia.

namespace LinqFindVsWhere
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> list = new List<string>();
            list.AddRange(new string[]
            {
                "item1",
                "item2",
                "item3",
                "item4"
            });

            string item2 = list.Find(x => x == "item2");
            Console.WriteLine(item2 == null ? "not found" : "found");
            string item3 = list.Where(x => x == "item3").FirstOrDefault();
            Console.WriteLine(item3 == null ? "not found" : "found");
            Console.ReadKey();
        }
    }
}
Rey de los hipócritas
fuente
45
FWIW, list.FirstOrDefault(x => x == "item3");es más conciso que usar ambos .Wherey .FirstOrDefault.
Kirk Woll
@ Kirk, creo que mi siguiente pregunta sería por qué agregaron el hallazgo. Ese es un buen consejo. Lo único que se me ocurre es que FirstOrDefault podría devolver un valor predeterminado diferente a nulo. De lo contrario, parece una adición sin sentido.
KingOfHypocrites
8
Findes anterior a LINQ. (estaba disponible en .NET 2.0 y no podía usar lambdas. Se vio obligado a usar métodos normales o métodos anónimos)
Kirk Woll

Respuestas:

205

¿Dónde está el Findmétodo IEnumerable<T>? (Pregunta retórica.)

El Wherey FirstOrDefaultmétodos son aplicables contra múltiples tipos de secuencias, incluyendo List<T>, T[], Collection<T>, etc. Cualquier secuencia que implementos IEnumerable<T>pueden utilizar estos métodos. Findestá disponible solo para List<T>. Los métodos que generalmente son más aplicables, son más reutilizables y tienen un mayor impacto.

Supongo que mi siguiente pregunta sería por qué agregaron el hallazgo. Ese es un buen consejo. Lo único que se me ocurre es que FirstOrDefault podría devolver un valor predeterminado diferente a nulo. De lo contrario, parece una adición sin sentido

Finden List<T>anteriores a los otros métodos. List<T>fue agregado con genéricos en .NET 2.0, y Findfue parte de la API para esa clase. Wherey FirstOrDefaultse agregaron como métodos de extensión IEnumerable<T>con Linq, que es una versión posterior de .NET. No puedo decir con certeza que si Linq existiera con la versión 2.0 Find, nunca se habría agregado, pero ese podría ser el caso de muchas otras características que aparecieron en versiones anteriores de .NET que quedaron obsoletas o redundantes en versiones posteriores.

Anthony Pegram
fuente
85
Solo para complementar: no es necesario llamar a Where y First o FirstOrDefault: First o FirstOrDefault le permite especificar un predicado de búsqueda, lo que hace innecesaria la llamada Where
Robson Rocha
44
Pero Where(condition).FirstOrDefault()optimiza al menos tan bien y, a veces, mejor que FirstOrDefault(condition)solo. Siempre utilizamos Where()para obtener un rendimiento mejorado cuando está disponible.
Suncat2000
77
@ Suncat2000 proporciona un ejemplo, por favor
Konstantin Salavatov
2
@ Suncat2000 Está utilizando Linq debido a su poder expresivo y desea escribir código declarativo. No debe preocuparse por tales micro mejoras, que también pueden cambiar en implementaciones futuras. Además, no optimices demasiado temprano
Piotr Falkowski
50

Me acabo de enterar hoy, haciendo algunas pruebas en una lista de objetos de 80K y descubrí que Find()puede ser hasta un 1000% más rápido que usar un Wherecon FirstOrDefault(). No lo sabía hasta probar un temporizador antes y después de cada uno. A veces era al mismo tiempo, de lo contrario era más rápido.

digiben
fuente
66
¿Lo probaste con Where AND FirstOrDefault? Si tal vez lo intentó con solo FirstOrDefault y vea si Find () es aún mejor.
MVCKarl
55
Parece que no materializó el resultado con .ToList()o .ToArray()para realizar la consulta.
Andrew Morton
44
Esto se debe a Findque utiliza las claves primarias (por lo tanto, los índices), mientras que Wherees una consulta SQL simple
percepbus del
44
A partir de EF6, Find y FirstOrDefault generan exactamente las mismas instrucciones SQL. Puede ver el SQL en una aplicación de consola haciendo context.Database.Log = Console.Write; El ejemplo localizado está usando "Buscar" en la memoria contra una lista de cadenas, no contra una base de datos con claves primarias. Quizás la traducción de la declaración de la cláusula Find, que omite la necesidad de realizar el análisis de expresiones lambda, es la razón de la mejora del rendimiento en este caso. Contra una base de datos que dudo que usted notará una diferencia en situaciones RL ... También me pregunto si fue probada usando Encuentra primera vez de la segunda ...
C.List
2
Bueno, esta mejora de rendimiento se debe a que find () verifica en la memoria caché el objeto antes de presionar DB mientras que where () siempre va a DB para obtener el objeto
Gaurav
35

Hay una diferencia muy importante si la fuente de los datos es Entity Framework: Findencontrará entidades en el estado 'agregado' que aún no persisten, pero Whereque no lo harán. Esto es por diseño.

Gredoso
fuente
1

además de la respuesta de Anthony, Where()visite todos los registros y luego devuelva los resultados, mientras que Find()no es necesario recorrer todos los registros si el predicado coincide con el predicado dado.

Supongamos que tiene una lista de la clase de prueba que tiene idy namepropiedades.

 List<Test> tests = new List<Test>();
 tests.Add(new Test() { Id = 1, Name = "name1" });
 tests.Add(new Test() { Id = 2, Name = "name2" });
 tests.Add(new Test() { Id = 3, Name = "name3" });
 tests.Add(new Test() { Id = 4, Name = "name2" }); 
 var r = tests.Find(p => p.Name == "name2");
 Console.WriteLine(r.Id);

Dará salida de 2, y solo se necesitan 2 visitas Buscar para dar resultado, pero si lo usa Where().FirstOrDefault(), visitaremos todos los registros y luego obtendremos resultados.

Entonces, cuando sabes que solo quieres el primer resultado de los registros en la colección Find(), será más adecuado queWhere().FirtorDefault();

M Muneeb Ijaz
fuente
44
pero si usa Where (). FirstOrDefault () visitaremos todos los registros y luego obtendremos resultados. No FirstOrDefault'burbujeará' la cadena y dejará de enumerar todo. Utilizo el término 'burbuja' por falta de una mejor expresión, porque en realidad cada selector / predicado se pasará al siguiente, por lo que el último método de la cadena realmente está haciendo el trabajo primero.
Silvermind
1

Wow, acabo de ver el tutorial de EF de MicrosofToolbox hoy en Youtube. Dijo sobre el uso de Find () y FirstOrDefault (condición) en la consulta y Find () buscará los datos que haya realizado algo en ese objeto (agregar o editar o eliminar, pero aún no se ha guardado en la base de datos), mientras que FirstOrDefault solo busca lo que ya se ha guardado

nguyen khanh
fuente
-1

Find()es el equivalente IEnumerable de a FirstOrDefault(). No debe encadenar ambos .Where () con .FirstOrDefault()porque .Where()recorre toda la matriz y luego recorrerá esa lista para encontrar el primer elemento. Ahorras una cantidad increíble de tiempo al poner tu predicado de búsqueda en el FirstOrDefault()método.

Además, le animo a leer la pregunta vinculada a este hilo para conocer más acerca de los mejores rendimientos en el uso de los .Find()escenarios específicos Rendimiento de Find () vs. FirstOrDefault ()

Latigazo
fuente
Este es un duplicado de la respuesta sobre la tuya. Además, vea el comentario de Silvermind sobre esa respuesta.
carlin.scott