É claro que um desempenho de pesquisa da HashSet<T>
classe genérica é maior que o da List<T>
classe genérica . Basta comparar a chave baseada em hash com a abordagem linear na List<T>
classe.
No entanto, o cálculo de uma chave de hash pode levar alguns ciclos de CPU, portanto, para uma pequena quantidade de itens, a pesquisa linear pode ser uma alternativa real à HashSet<T>
.
Minha pergunta: onde está o ponto de equilíbrio?
Para simplificar o cenário (e para ser justo), vamos supor que a List<T>
classe use o Equals()
método do elemento para identificar um item.
.net
performance
collections
list
hash
Michael Damatov
fonte
fonte
Respostas:
Muitas pessoas estão dizendo que quando você chega ao tamanho em que a velocidade é realmente uma preocupação que
HashSet<T>
sempre superaList<T>
, mas isso depende do que você está fazendo.Digamos que você tenha um
List<T>
que só terá em média 5 itens. Durante um grande número de ciclos, se um único item for adicionado ou removido a cada ciclo, é melhor usar aList<T>
.Fiz um teste para isso na minha máquina e, bem, tem que ser muito pequeno para obter vantagem
List<T>
. Para uma lista de cadeias curtas, a vantagem desapareceu após o tamanho 5, para objetos após o tamanho 20.Aqui estão os dados exibidos como um gráfico:
Aqui está o código:
fonte
List<T>
de um mecanismo de jogo e, como normalmente terei um alto volume de objetos, esse tipo de coleção seria perfeito.Você está vendo isso errado. Sim, uma pesquisa linear de uma lista superará um HashSet para um pequeno número de itens. Mas a diferença de desempenho geralmente não importa para coleções tão pequenas. Geralmente, é com as grandes coleções que você precisa se preocupar, e é aí que você pensa em termos de Big-O . No entanto, se você tiver medido um gargalo real no desempenho do HashSet, poderá tentar criar um híbrido List / HashSet, mas fará isso realizando muitos testes de desempenho empíricos - sem fazer perguntas sobre o SO.
fonte
when small collection becomes large enough to worry about HashSet vs List?
dezenas, dezenas de milhares, bilhões de elementos?HashSet<T>
. Nos casos de pequeno número em queList<T>
pode ser mais rápido, a diferença é insignificante. . "É essencialmente inútil comparar duas estruturas de desempenho que se comportam de maneira diferente. Use a estrutura que transmite a intenção. Mesmo se você disser
List<T>
que não teria duplicatas e a ordem de iteração não for comparável a aHashSet<T>
, ainda é uma má escolha para usar,List<T>
porque é relativamente menos tolerante a falhas.Dito isto, vou inspecionar alguns outros aspectos do desempenho,
Embora a adição seja O (1) nos dois casos, será relativamente mais lento no HashSet, pois envolve o custo da pré-computação do código hash antes de armazená-lo.
A escalabilidade superior do HashSet tem um custo de memória. Cada entrada é armazenada como um novo objeto, juntamente com seu código de hash. Este artigo pode lhe dar uma ideia.
fonte
A utilização de um HashSet <> ou Lista <> se resume a como você precisa acessar sua coleção . Se você precisar garantir a ordem dos itens, use uma Lista. Caso contrário, use um HashSet. Deixe a Microsoft se preocupar com a implementação de seus algoritmos e objetos de hash.
Um HashSet acessará itens sem precisar enumerar a coleção (complexidade de O (1) ou próximo a ela) e, como uma Lista garante ordem, diferentemente de um HashSet, alguns itens terão que ser enumerados (complexidade de O (n)).
fonte
List
é preferível um, porque você pode se lembrar de um índice - essa é a situação em que você estão descrevendo.Apenas pensei em usar algumas referências para diferentes cenários para ilustrar as respostas anteriores:
E para cada cenário, procurando valores que aparecem:
Antes de cada cenário, eu gerava listas de seqüências aleatórias de tamanho aleatório e depois alimentava cada lista com um hashset. Cada cenário foi executado 10.000 vezes, essencialmente:
(pseudocódigo de teste)
Saída de amostra
Testado no Windows 7, 12 GB de RAM, 64 bits, Xeon 2,8 GHz
fonte
List
ainda leve apenas 0,17 milissegundos para executar uma única pesquisa e provavelmente não será necessário substituirHashSet
até que a frequência de pesquisa atinja níveis absurdos. Até então, o uso da lista geralmente é o menor dos problemas.O ponto de equilíbrio dependerá do custo de computação do hash. Os cálculos de hash podem ser triviais ou não ... :-) Sempre existe a classe System.Collections.Specialized.HybridDictionary para ajudá-lo a não precisar se preocupar com o ponto de equilíbrio.
fonte
A resposta, como sempre, é " depende ". Presumo que as tags que você está falando sobre C #.
Sua melhor aposta é determinar
e escreva alguns casos de teste.
Também depende de como você classifica a lista (se é que ela é classificada), que tipo de comparações precisam ser feitas, quanto tempo a operação "Comparar" leva para o objeto específico na lista, ou mesmo como você pretende usar o coleção.
Geralmente, o melhor a escolher não se baseia tanto no tamanho dos dados com os quais você está trabalhando, mas na maneira como pretende acessá-los. Você tem cada dado associado a uma sequência específica ou a outros dados? Uma coleção baseada em hash provavelmente seria a melhor. A ordem dos dados que você está armazenando é importante ou você precisará acessar todos os dados ao mesmo tempo? Uma lista regular pode ser melhor então.
Adicional:
Obviamente, meus comentários acima assumem 'desempenho' significa acesso a dados. Outra coisa a considerar: o que você procura quando diz "desempenho"? O valor individual do desempenho é procurado? É gerenciamento de grandes conjuntos de valores (10000, 100000 ou mais)? É o desempenho de preencher a estrutura de dados com dados? Removendo dados? Acessando bits individuais de dados? Substituindo valores? Iterando sobre os valores? Uso de memória? Velocidade de cópia de dados? Por exemplo, se você acessar dados por um valor de cadeia, mas seu principal requisito de desempenho for o uso mínimo de memória, poderá haver problemas de design conflitantes.
fonte
Você pode usar um HybridDictionary que detecta automaticamente o ponto de interrupção e aceita valores nulos, tornando-o essencialmente o mesmo que um HashSet.
fonte
Depende. Se a resposta exata realmente importa, faça alguns perfis e descubra. Se você tiver certeza de que nunca terá mais que um certo número de elementos no conjunto, escolha uma Lista. Se o número for ilimitado, use um HashSet.
fonte
Depende do que você está usando. Se suas chaves forem números inteiros, você provavelmente não precisará de muitos itens antes que o HashSet seja mais rápido. Se você o estiver digitando em uma sequência, será mais lento e depende da sequência de entrada.
Certamente você poderia criar uma referência facilmente?
fonte
Um fator que você não leva em consideração é a robustez da função GetHashcode (). Com uma função de hash perfeita, o HashSet terá claramente um melhor desempenho de pesquisa. Mas, à medida que a função hash diminui, o tempo de pesquisa do HashSet também diminui.
fonte
Depende de muitos fatores ... Implementação da lista, arquitetura da CPU, JVM, semântica do loop, complexidade do método equals, etc. as pesquisas superam as pesquisas lineares sem controle e a diferença só aumenta a partir daí.
Espero que isto ajude!
fonte