Ao usar ToList()
, há um impacto no desempenho que precisa ser considerado?
Eu estava escrevendo uma consulta para recuperar arquivos de um diretório, que é a consulta:
string[] imageArray = Directory.GetFiles(directory);
No entanto, desde que eu gosto de trabalhar List<>
, decidi colocar ...
List<string> imageList = Directory.GetFiles(directory).ToList();
Portanto, existe algum tipo de impacto no desempenho que deve ser considerado ao decidir fazer uma conversão como essa - ou somente quando se lida com um grande número de arquivos? Esta é uma conversão insignificante?
c#
arrays
performance
list
Cody
fonte
fonte
List<T>
favor de aT[]
se tornasse o código mais lógico / legível / sustentável (a menos que a conversão estivesse causando problemas visíveis de desempenho, nesse caso, eu visitá-lo, eu acho).Add
ouRemove
, gostaria de deixá-lo comoIEnumerable<T>
(ou até melhorvar
)EnumerateFiles
vez deGetFiles
, para que apenas uma matriz seja criada.GetFiles(directory)
, como é implementado no .NET atualmente, praticamente faznew List<string>(EnumerateFiles(directory)).ToArray()
. Então,GetFiles(directory).ToList()
cria uma lista, cria uma matriz a partir disso e cria uma lista novamente. Como 2kay diz, você deve preferir fazerEnumerateFiles(directory).ToList()
aqui.Respostas:
IEnumerable.ToList()
Sim,
IEnumerable<T>.ToList()
tem um impacto no desempenho, é uma operação O (n) , embora provavelmente exija apenas atenção em operações críticas de desempenho.A
ToList()
operação usará oList(IEnumerable<T> collection)
construtor. Este construtor deve fazer uma cópia da matriz (de maneira mais geralIEnumerable<T>
), caso contrário, futuras modificações da matriz original também serão alteradas na fonte, oT[]
que geralmente não seria desejável.Gostaria de reiterar que isso só fará diferença com uma lista enorme, pois copiar pedaços de memória é uma operação bastante rápida de executar.
Dica útil,
As
vsTo
Você notará no LINQ que existem vários métodos que começam com
As
(comoAsEnumerable()
) eTo
(comoToList()
). Os métodos iniciados comTo
exigem uma conversão como acima (isto é, podem afetar o desempenho), e os métodos iniciados comAs
não exigem e apenas exigirão alguma operação simples ou de conversão .Detalhes adicionais sobre
List<T>
Aqui está um pouco mais detalhadamente de como
List<T>
funciona, caso você esteja interessado :)A
List<T>
também usa uma construção chamada matriz dinâmica que precisa ser redimensionada sob demanda; esse evento de redimensionamento copia o conteúdo de uma matriz antiga para a nova matriz. Por isso, começa pequeno e aumenta de tamanho, se necessário .Essa é a diferença entre os atributos
Capacity
e . refere-se ao tamanho da matriz nos bastidores, é o número de itens nos quais é sempre . Portanto, quando um item é adicionado à lista, aumentando-o , o tamanho do é dobrado e a matriz é copiada.Count
List<T>
Capacity
Count
List<T>
<= Capacity
Capacity
List<T>
fonte
List(IEnumerable<T> collection)
construtor verifica se o parâmetro de coleção éICollection<T>
e cria uma nova matriz interna com o tamanho necessário imediatamente. Se a coleção de parâmetros não forICollection<T>
, o construtor itera e chamaAdd
cada elemento.Sim, claro. Teoricamente, mesmo
i++
tendo um impacto no desempenho, ele atrasa o programa por talvez alguns tiques.O que
.ToList
faz?Quando você invoca
.ToList
, o código chamaEnumerable.ToList()
que é um método de extensão quereturn new List<TSource>(source)
. No construtor correspondente, na pior circunstância, ele passa pelo contêiner de itens e os adiciona um a um em um novo contêiner. Portanto, seu comportamento afeta pouco o desempenho. É impossível ser um gargalo de desempenho do seu aplicativo.O que há de errado com o código na pergunta
Directory.GetFiles
passa pela pasta e retorna os nomes de todos os arquivos imediatamente para a memória, corre o risco de que a string [] gaste muita memória, diminuindo a velocidade de tudo.O que deve ser feito então
Depende. Se você (assim como sua lógica comercial) garantir que o valor do arquivo na pasta seja sempre pequeno, o código será aceitável. Mas ainda é sugerido o uso de uma versão lenta:
Directory.EnumerateFiles
em C # 4. Isso é muito mais parecido com uma consulta, que não será executada imediatamente, você pode adicionar mais consultas como:que irá parar de procurar o caminho assim que um arquivo cujo nome contenha "meuarquivo" for encontrado. Obviamente, isso tem um desempenho melhor então
.GetFiles
.fonte
Sim existe. O uso do método de extensão
Enumerable.ToList()
criará um novoList<T>
objeto a partir daIEnumerable<T>
coleção de fontes que, é claro, tem um impacto no desempenho.No entanto, o entendimento
List<T>
pode ajudá-lo a determinar se o impacto no desempenho é significativo.List<T>
usa um array (T[]
) para armazenar os elementos da lista. As matrizes não podem ser estendidas depois de serem alocadas, portantoList<T>
, usará uma matriz de tamanho grande para armazenar os elementos da lista. Quando oList<T>
tamanho aumenta para além do tamanho da matriz subjacente, uma nova matriz precisa ser alocada e o conteúdo da matriz antiga deve ser copiado para a nova matriz maior antes que a lista possa crescer.Quando um novo
List<T>
é construído a partir de um,IEnumerable<T>
existem dois casos:A coleção de origem implementa
ICollection<T>
: ThenICollection<T>.Count
é usada para obter o tamanho exato da coleção de origem e uma matriz de apoio correspondente é alocada antes de todos os elementos da coleção de origem serem copiados para a matriz de apoio usandoICollection<T>.CopyTo()
. Esta operação é bastante eficiente e provavelmente será mapeada para algumas instruções da CPU para copiar blocos de memória. No entanto, em termos de desempenho, a memória é necessária para a nova matriz e os ciclos da CPU são necessários para copiar todos os elementos.Caso contrário, o tamanho da coleção de fontes é desconhecido e o enumerador de
IEnumerable<T>
é usado para adicionar cada elemento de origem, um de cada vez, ao novoList<T>
. Inicialmente, a matriz de apoio está vazia e uma matriz de tamanho 4 é criada. Então, quando essa matriz é muito pequena, o tamanho é dobrado, aumentando assim a matriz de apoio 4, 8, 16, 32 etc. Toda vez que a matriz de apoio cresce, ela deve ser realocada e todos os elementos armazenados até agora devem ser copiados. Essa operação é muito mais cara em comparação com o primeiro caso em que uma matriz do tamanho correto pode ser criada imediatamente.Além disso, se sua coleção de fontes contiver, digamos, 33 elementos, a lista terminará usando uma matriz de 64 elementos que estão desperdiçando alguma memória.
No seu caso, a coleção de fontes é uma matriz implementada
ICollection<T>
para que o impacto no desempenho não seja algo com que você deva se preocupar, a menos que sua matriz de origem seja muito grande. A chamadaToList()
simplesmente copiará a matriz de origem e a envolverá em umList<T>
objeto. Mesmo o desempenho do segundo caso não é motivo de preocupação para pequenas coleções.fonte
O problema com seu cenário preciso é que, em primeiro lugar, sua verdadeira preocupação com o desempenho seria a velocidade e a eficiência do disco rígido do cache da unidade.
Nessa perspectiva, o impacto é certamente insignificante, a ponto de NÃO ser necessário considerar.
MAS SOMENTE se você realmente precisar dos recursos da
List<>
estrutura para torná-lo mais produtivo ou seu algoritmo mais amigável ou outra vantagem. Caso contrário, você está apenas propositalmente adicionando um desempenho insignificante, sem nenhuma razão. Nesse caso, naturalmente, você não deve fazê-lo! :)fonte
ToList()
cria uma nova lista e coloca os elementos nela, o que significa que há um custo associado ao fazerToList()
. No caso de coleção pequena, não será um custo muito perceptível, mas ter uma coleção enorme pode causar um impacto no desempenho no caso de usar a ToList.Geralmente você não deve usar ToList (), a menos que o trabalho que você está fazendo não possa ser realizado sem converter a coleção em Lista. Por exemplo, se você deseja apenas percorrer a coleção, não precisa executar a ToList
Se você estiver executando consultas em uma fonte de dados, por exemplo, um banco de dados usando LINQ to SQL, o custo de fazer a ToList é muito mais alto porque, quando você usa a ToList com LINQ to SQL em vez de executar a execução atrasada, ou seja, carregar itens quando necessário (o que pode ser benéfico em muitos cenários) carrega instantaneamente itens do banco de dados na memória
fonte
Será tão (in) eficiente quanto fazer:
Se você desmontar o código-fonte do construtor que recebe um
IEnumerable<T>
, verá que ele fará algumas coisas:Chamada
collection.Count
, portanto, secollection
for umIEnumerable<T>
, forçará a execução. Secollection
é uma matriz, lista, etc., deve serO(1)
.Se
collection
implementadoICollection<T>
, ele salvará os itens em uma matriz interna usando oICollection<T>.CopyTo
método Ele deve serO(n)
, sendon
o comprimento da coleção.Se
collection
não for implementadoICollection<T>
, ele percorrerá os itens da coleção e os adicionará a uma lista interna.Portanto, sim, ele consumirá mais memória, pois precisará criar uma nova lista e, na pior das hipóteses, será
O(n)
, pois irá percorrer a páginacollection
para fazer uma cópia de cada elemento.fonte
0(n)
onden
é a soma total de bytes as cadeias na coleção original ocupam, e não a contagem dos elementos (bem para ser mais exacto n = bytes / palavra tamanho)bool
,int
Etc.)? Você realmente não precisa fazer uma cópia de cada sequência na coleção. Você acabou de adicioná-los à nova lista.Considerando o desempenho da recuperação da lista de arquivos,
ToList()
é insignificante. Mas não para outros cenários. Isso realmente depende de onde você o está usando.Ao chamar uma matriz, lista ou outra coleção, você cria uma cópia da coleção como uma
List<T>
. O desempenho aqui depende do tamanho da lista. Você deve fazê-lo quando realmente necessário.No seu exemplo, você o chama em uma matriz. Ele itera sobre a matriz e adiciona os itens um por um a uma lista recém-criada. Portanto, o impacto no desempenho depende do número de arquivos.
Ao chamar um
IEnumerable<T>
, você materializa oIEnumerable<T>
(geralmente uma consulta).fonte
ToList Criará uma nova lista e copiará os elementos da fonte original para a lista recém-criada, portanto, o único é copiar os elementos da fonte original e depender do tamanho da fonte.
fonte