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

161

Costumo ver pessoas usando Where.FirstOrDefault()para fazer uma pesquisa e pegar o primeiro elemento. Por que não usar apenas Find()? Existe uma vantagem para o outro? Eu não sabia dizer a diferença.

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();
        }
    }
}
KingOfHypocrites
fonte
45
FWIW, list.FirstOrDefault(x => x == "item3");é mais conciso do que usar ambos .Wheree .FirstOrDefault.
21412 Kirk Woll
@ Kirk, acho que minha próxima pergunta seria por que eles acrescentaram a descoberta. Essa é uma boa dica. A única coisa em que consigo pensar é que o FirstOrDefault pode retornar um valor padrão diferente de null. Caso contrário, parece apenas uma adição inútil.
KingOfHypocrites
8
Findé anterior ao LINQ. (estava disponível no .NET 2.0 e você não poderia usar lambdas Você foi forçado a usar métodos normais ou métodos anônimos.)
Kirk Woll

Respostas:

204

Onde está o Findmétodo IEnumerable<T>? (Questão retórica.)

A Wheree FirstOrDefaultos métodos são aplicáveis contra vários tipos de sequências, incluindo List<T>, T[], Collection<T>, etc. Qualquer sequência que implementa IEnumerable<T>pode utilizar estes métodos. Findestá disponível apenas para o List<T>. Métodos geralmente mais aplicáveis, são mais reutilizáveis e têm maior impacto.

Acho que minha próxima pergunta seria por que eles acrescentaram a descoberta. Essa é uma boa dica. A única coisa em que consigo pensar é que o FirstOrDefault pode retornar um valor padrão diferente de null. Caso contrário, parece apenas uma adição inútil

Findon List<T>antecede os outros métodos. List<T>foi adicionado com genéricos no .NET 2.0 e Findfazia parte da API para essa classe. Wheree FirstOrDefaultforam adicionados como métodos de extensão para o IEnumerable<T>Linq, que é uma versão .NET posterior. Não posso dizer com certeza que, se o Linq existisse com a versão 2.0, isso Findnunca teria sido adicionado, mas esse é sem dúvida o caso de muitos outros recursos que vieram em versões anteriores do .NET que se tornaram obsoletos ou redundantes em versões posteriores.

Anthony Pegram
fonte
85
Apenas para complementar: Não há necessidade de chamar Onde e Primeira ou FirstOrDefault: em primeiro ou FirstOrDefault permite que você especifique um predicado de pesquisa, tornando o Onde chamada desnecessária
Robson Rocha
4
Mas Where(condition).FirstOrDefault()otimiza pelo menos também e, às vezes, melhor do que FirstOrDefault(condition)sozinho. Sempre usamos Where()o desempenho aprimorado quando disponível.
Suncat2000
7
@ Suncat2000 forneça um exemplo, por favor #
Konstantin Salavatov
2
@ Suncat2000 Você está usando o Linq devido ao seu poder expressivo e deseja escrever código declarativo. Você não deve se preocupar com essas micro melhorias, que também podem mudar em futuras implementações. Além disso, não otimize muito cedo
Piotr Falkowski
50

Acabei de descobrir hoje, fazendo alguns testes em uma lista de objetos de 80K e descobri que Find()pode ser até 1000% mais rápido do que usar um Wherecom FirstOrDefault(). Eu não sabia disso até testar um cronômetro antes e depois de cada um. Às vezes era a mesma hora, caso contrário, era mais rápido.

digiben
fonte
6
Você tentou com Where AND FirstOrDefault? Se você tentou, talvez apenas com FirstOrDefault e veja se Find () ainda é melhor.
MVCKarl
5
Parece que você não materializou o resultado com uma .ToList()ou .ToArray()para realmente executar a consulta.
Andrew Morton
4
É porque Findfaz uso das chaves primárias (portanto, índices), enquanto que Whereé uma consulta sql simples
percebus
4
No EF6, Find e FirstOrDefault geram exatamente as mesmas instruções SQL. Você pode ver o SQL em um aplicativo de console executando context.Database.Log = Console.Write; O exemplo descrito está usando na memória "Localizar" em uma lista de seqüências de caracteres, não indo em um banco de dados com chaves primárias. Talvez a tradução da declaração da cláusula Find - que omite a necessidade de analisar a expressão lambda seja a razão da melhoria de desempenho nesse caso. Em relação a um banco de dados, duvido que você notará uma diferença nas situações de RL ... Também me pergunto se foi testado usando Localizar primeiro em vez de segundo ...
C.List
2
Bem, esta melhoria de desempenho é porque find () check-in de cache para o objeto antes de bater DB que, no caso () sempre ir DB para obter objeto
Gaurav
35

Há uma diferença muito importante se a fonte dos dados for Entity Framework: Findencontrará entidades no estado 'adicionado' que ainda não foram persistidas, mas Wherenão serão. Isso é por design.

Chalky
fonte
1

além de Anthony responder à Where()visita através de todos os registros e, em seguida, retornar resultado (s) enquantoFind() não precisar percorrer todos os registros, se o predicado corresponder ao predicado especificado.

digamos que você tenha propriedades ide namepropriedades da classe List of Test .

 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á saída de 2, e apenas 2 visitas são necessárias para dar resultado, mas se você usarWhere().FirstOrDefault() , visitaremos todos os registros e obteremos resultados.

Portanto, quando você sabe que deseja apenas o primeiro resultado dos registros na coleção Find(), será mais adequado do queWhere().FirtorDefault();

M Muneeb Ijaz
fonte
4
mas se você usar Where (). FirstOrDefault () visitaremos todos os registros e obteremos resultados. Não. FirstOrDefault'borbulha' a cadeia e para de enumerar tudo. Uso o termo 'bolha' por falta de uma expressão melhor, porque na verdade todos os seletores / predicados serão passados ​​para o próximo, portanto, o último método da cadeia está realmente trabalhando primeiro.
Silvermind
1

Uau, acabei de assistir ao tutorial da EF no MicrosofToolbox hoje no Youtube. Ele disse sobre o uso de Find () e FirstOrDefault (condição) na consulta e Find () procurará os dados que você executou algo nesse objeto (adicionar ou editar ou excluir - mas ainda não salvos no banco de dados) enquanto o FirstOrDefault apenas procure o que já foi salvo

nguyen khanh
fonte
-1

Find()é o equivalente IEnumerable de a FirstOrDefault(). Você não deve encadear os dois .Where () .FirstOrDefault()porque ele .Where()passa por toda a matriz e, em seguida, percorre a lista para encontrar o primeiro item. Você economiza uma quantidade incrível de tempo colocando seu predicado de pesquisa no FirstOrDefault()método.

Além disso, incentivo você a ler a pergunta vinculada a este tópico para saber mais sobre os melhores desempenhos no uso de .Find()cenários específicos. Desempenho de Find () vs. FirstOrDefault ()

Whiplash
fonte
Esta é uma duplicata da resposta acima da sua. Veja também o comentário de Silvermind sobre essa resposta.
carlin.scott 15/01