C # LINQ Primeiro () mais rápido que ToArray () ?

Eu estou executando um teste.

Parece que:

Método 1)

List = new List{1,2,4, .....} //assume 1000k var result ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x)).First(); 

método 2)

 List = new List{1,2,4, .....} //assume 1000k var result = ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x)).ToArray()[0]; 

Por que o método 2 é tão lento comparado ao método 1?

Erm … porque você está criando uma matriz extra (ao invés de apenas usar o iterador). A primeira abordagem é interrompida após a primeira correspondência ( Where é uma API de streaming sem buffer). O segundo carrega todas as correspondências em uma matriz (presumivelmente com várias redimensiones) e, em seguida, pega o primeiro item.

Como uma nota rodapé; você pode criar sequências infinitas; a primeira abordagem ainda funcionaria, a segunda funcionaria para sempre (ou explodiria).

Também poderia ser:

 var result ErrorCodes.First(x => ReturnedErrorCodes.Contains(x)); 

(Isso não vai tornar mais rápido, mas talvez seja mais fácil de ler)

Você tem um jarro contendo mil moedas, muitas das quais são moedas. Você quer um centavo. Aqui estão dois methods para resolver seu problema:

  1. Puxe moedas para fora do flask, uma de cada vez, até obter um centavo. Agora você tem um centavo.

  2. Puxe moedas do pote, uma de cada vez, colocando as moedas em outro pote. Se esse flask for pequeno demais, mova-os, um de cada vez, para um flask maior. Continue fazendo isso até ter todas as moedas no pote final. Esse pote é provavelmente muito grande. Fabrique um pote que seja exatamente grande o suficiente para conter as moedas e mova as moedas, uma de cada vez, para o novo pote. Agora comece a tirar moedas desse pote. Pegue o primeiro. Agora você tem um centavo.

Agora está claro porque o método 1 é muito mais rápido que o método 2?

Por causa da execução adiada.

O código ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x)) não retorna uma coleção de inteiros, em vez disso, ele retorna uma expressão que é capaz de retornar um stream de inteiros. Ele não faz nenhum trabalho real até você começar a ler inteiros dele.

O método ToArray consumirá todo o stream e colocará todos os inteiros em uma matriz. Isso significa que todos os itens da lista inteira devem ser comparados aos códigos de erro.

O First método, por outro lado, somente obterá o primeiro item do stream e, em seguida, interromperá a leitura do stream. Isso tornará muito mais rápido, pois interromperá a comparação de itens da lista com os códigos de erro assim que encontrar uma correspondência.

Porque ToArray() copia a seqüência inteira para uma matriz.

O método 2 tem que percorrer toda a seqüência para criar uma matriz e, em seguida, retorna o primeiro elemento.

O método 1 apenas itera sobre o suficiente da sequência para encontrar o primeiro elemento correspondente.

ToArray() percorre toda a seqüência que foi dada e cria e cria uma matriz.

Se você não chamar ToArray() , First() permite que Where() retorne apenas o primeiro item que corresponda e retorne imediatamente.

Primeiro () é a complexidade de O (1)

ToArray () [0] é a complexidade O (n) +1

 var @e = array.GetEnumerator(); // First @e.MoveNext(); return @e.Current; // ToArray (with yield [0] should as fast as First...) while (@e.MoveNext() { yield return @e.Current; } 

Porque no segundo exemplo, você está realmente convertendo o IEnumerable para uma matriz, enquanto no primeiro exemplo, nenhuma conversão está ocorrendo.

No método 2, toda a matriz deve ser convertida em uma matriz primeiro. Além disso, parece estranho misturar o access à matriz quando First() é muito mais legível.

Isso faz sentido, ToArray provavelmente envolve uma cópia, que sempre será mais cara, já que o Linq não pode dar nenhuma garantia sobre como você vai usar seu array, enquanto o First () pode simplesmente retornar o único elemento no array. começando.