O HashSet é muito mais rápido que o TreeSet (tempo constante versus tempo de log para a maioria das operações, como adicionar, remover e conter), mas não oferece garantias de pedidos como o TreeSet.
- a classe oferece desempenho de tempo constante para as operações básicas (adicionar, remover, conter e tamanho).
- não garante que a ordem dos elementos permaneça constante ao longo do tempo
- O desempenho da iteração depende da capacidade inicial e do fator de carga do HashSet.
- É bastante seguro aceitar o fator de carga padrão, mas convém especificar uma capacidade inicial com o dobro do tamanho em que você espera que o conjunto cresça.
- garante o log (n) custo do tempo para as operações básicas (adicionar, remover e conter)
- garante que os elementos do conjunto sejam classificados (ascendente, natural ou o especificado por você através de seu construtor) (implementa
SortedSet
)
- não oferece nenhum parâmetro de ajuste para o desempenho da iteração
- oferece alguns métodos úteis para lidar com o conjunto ordenado como
first()
, last()
, headSet()
, e tailSet()
etc
Pontos importantes:
- Ambos garantem uma coleção de elementos sem duplicação
- Geralmente é mais rápido adicionar elementos ao HashSet e depois converter a coleção em um TreeSet para uma travessia classificada sem duplicação.
- Nenhuma dessas implementações é sincronizada. Ou seja, se vários encadeamentos acessam um conjunto simultaneamente e pelo menos um dos encadeamentos modifica o conjunto, ele deve ser sincronizado externamente.
- LinkedHashSet é, em certo sentido, intermediário entre
HashSet
e TreeSet
. Implementado como uma tabela de hash com uma lista vinculada em execução, no entanto, fornece iteração ordenada por inserção que não é igual à travessia classificada garantida pelo TreeSet .
Portanto, a escolha do uso depende inteiramente de suas necessidades, mas acho que, mesmo que você precise de uma coleção ordenada, ainda deve preferir o HashSet para criar o conjunto e depois convertê-lo em TreeSet.
- por exemplo
SortedSet<String> s = new TreeSet<String>(hashSet);
Uma vantagem ainda não mencionada de a
TreeSet
é que ela possui uma "localidade" maior, o que é uma abreviação para dizer (1) se duas entradas estão próximas na ordem, asTreeSet
coloca próximas umas das outras na estrutura de dados e, portanto, na memória; e (2) esse posicionamento tira proveito do princípio de localidade, que diz que dados semelhantes são frequentemente acessados por um aplicativo com frequência semelhante.Isso contrasta com a
HashSet
, que espalha as entradas por toda a memória, independentemente de quais sejam suas chaves.Quando o custo de latência da leitura de um disco rígido é milhares de vezes o custo da leitura do cache ou da RAM e quando os dados são realmente acessados com a localidade,
TreeSet
pode ser uma escolha muito melhor.fonte
TreeSet
/ do OpenJDKTreeMap
não é otimizada para localidade. Embora seja possível usar uma árvore b da ordem 4 para representar uma árvore vermelho-preta e, assim, melhorar o desempenho da localidade e do cache, não é assim que a implementação funciona. Em vez disso, cada nó armazena um ponteiro para sua própria chave, seu próprio valor, seu pai e seus nós filhos esquerdo e direito, evidentes no código-fonte JDK 8 para TreeMap.Entry .HashSet
é O (1) para acessar elementos, então isso certamente importa. Mas manter a ordem dos objetos no conjunto não é possível.TreeSet
é útil se a manutenção de um pedido (em termos de valores e não do pedido de inserção) for importante para você. Mas, como você observou, você está negociando uma ordem por um tempo mais lento para acessar um elemento: O (log n) para operações básicas.Dos javadocs para
TreeSet
:fonte
1.HashSet permite objeto nulo.
2.TreeSet não permitirá objeto nulo. Se você tentar adicionar valor nulo, ele lançará uma NullPointerException.
3.HashSet é muito mais rápido que TreeSet.
por exemplo
fonte
null
ao seu conjunto de qualquer maneira.TreeSet<String> badassTreeSet = new TreeSet<String>(new Comparator<String>() { public int compare(String string1, String string2) { if (string1 == null) { return (string2 == null) ? 0 : -1; } else if (string2 == null) { return 1; } else { return string1.compareTo(string2); } } }); badassTreeSet.add("tree"); badassTreeSet.add("asdf"); badassTreeSet.add(null); badassTreeSet.add(null); badassTreeSet.add("set"); badassTreeSet.add("tree"); System.out.println(badassTreeSet);
Baseando-se na adorável resposta visual do Maps por @shevchyk, aqui está minha opinião:
fonte
A razão pela qual a maioria usa
HashSet
é que as operações são (em média) O (1) em vez de O (log n). Se o aparelho contiver itens padrão, você não estará "brincando com as funções de hash", como foi feito para você. Se o conjunto contiver classes personalizadas, você precisará implementarhashCode
para usá-loHashSet
(embora o Java Efetivo mostre como), mas se você usar um,TreeSet
precisará fazê-loComparable
ou fornecer umComparator
. Isso pode ser um problema se a classe não tiver uma ordem específica.Às vezes, usei
TreeSet
(ou realmenteTreeMap
) para conjuntos / mapas muito pequenos (<10 itens), embora não tenha verificado se há algum ganho real ao fazê-lo. Para conjuntos grandes, a diferença pode ser considerável.Agora, se você precisar da classificação,
TreeSet
será apropriado, embora, mesmo assim, se as atualizações sejam frequentes e a necessidade de um resultado classificado seja pouco frequente, às vezes copiar o conteúdo para uma lista ou matriz e classificá-los pode ser mais rápido.fonte
Se você não estiver inserindo elementos suficientes para resultar em rehashings frequentes (ou colisões, se o seu HashSet não puder ser redimensionado), um HashSet certamente oferecerá o benefício do acesso em tempo constante. Mas em conjuntos com muito crescimento ou retração, você pode obter um desempenho melhor com o Treesets, dependendo da implementação.
O tempo amortizado pode estar próximo de O (1) com uma árvore vermelho-preta funcional, se a memória me servir. O livro de Okasaki teria uma explicação melhor do que eu consigo. (Ou veja sua lista de publicações )
fonte
As implementações do HashSet são, obviamente, muito, muito mais rápidas - menos sobrecarga porque não há pedidos. Uma boa análise das várias implementações do Conjunto em Java é fornecida em http://java.sun.com/docs/books/tutorial/collections/implementations/set.html .
A discussão lá também aponta uma interessante abordagem de "meio termo" para a questão Tree vs Hash. O Java fornece um LinkedHashSet, que é um HashSet com uma lista vinculada "orientada a inserção" sendo executada, ou seja, o último elemento da lista vinculada também é o mais recentemente inserido no Hash. Isso permite evitar a irregularidade de um hash não ordenado sem incorrer no aumento do custo de um TreeSet.
fonte
O TreeSet é uma das duas coleções classificadas (a outra é o TreeMap). Ele usa uma estrutura de árvore Vermelho-Preto (mas você sabia disso) e garante que os elementos estejam em ordem crescente, de acordo com a ordem natural. Opcionalmente, você pode construir um TreeSet com um construtor que permita fornecer à coleção suas próprias regras para o que o pedido deve ser (em vez de depender da ordem definida pela classe dos elementos) usando um Comparable ou Comparator
e Um LinkedHashSet é uma versão ordenada do HashSet que mantém uma lista duplamente vinculada em todos os elementos. Use esta classe em vez do HashSet quando se importar com a ordem da iteração. Quando você itera através de um HashSet, o pedido é imprevisível, enquanto um LinkedHashSet permite iterar pelos elementos na ordem em que foram inseridos.
fonte
Muitas respostas foram dadas, com base em considerações técnicas, especialmente em relação ao desempenho. Segundo mim, escolha entre
TreeSet
eHashSet
importa.Mas prefiro dizer que a escolha deve ser conduzida primeiro por considerações conceituais .
Se, para os objetos que você precisa manipular, uma ordem natural não faz sentido, não use
TreeSet
.É um conjunto classificado, pois é implementado
SortedSet
. Portanto, isso significa que você precisa substituir a funçãocompareTo
, que deve ser consistente com o que retorna a funçãoequals
. Por exemplo, se você tiver um conjunto de objetos de uma classe chamada Student, não creio que umTreeSet
faria sentido, uma vez que não há ordenação natural entre os alunos. Você pode encomendá-los pela nota média, ok, mas isso não é um "pedido natural". A funçãocompareTo
retornaria 0 não apenas quando dois objetos representam o mesmo aluno, mas também quando dois alunos diferentes têm a mesma nota. No segundo caso,equals
retornaria falso (a menos que você decida fazer com que o último retorne verdadeiro quando dois alunos diferentes tiverem a mesma nota, o que tornaria aequals
função um significado enganoso, sem dizer um significado errado.)Observe esta consistência entre
equals
ecompareTo
é opcional, mas altamente recomendado. Caso contrário, o contrato da interfaceSet
será quebrado, tornando seu código enganoso para outras pessoas, resultando também em comportamento inesperado.Esse link pode ser uma boa fonte de informações sobre esta questão.
fonte
Por que ter maçãs quando você pode comer laranjas?
Sério, garotos e garotas - se sua coleção é grande, lida e escrita em bilhões de vezes, e você está pagando por ciclos de CPU, então a escolha da coleção é relevante SOMENTE se você PRECISA ter um desempenho melhor. No entanto, na maioria dos casos, isso realmente não importa - alguns milissegundos aqui e ali passam despercebidos em termos humanos. Se realmente importava muito, por que você não está escrevendo código no assembler ou C? [indique outra discussão]. Portanto, o ponto é se você está feliz usando a coleção que escolheu e resolve o seu problema (mesmo que não seja especificamente o melhor tipo de coleção para a tarefa). O software é maleável. Otimize seu código sempre que necessário. Tio Bob diz que a otimização prematura é a raiz de todo mal. Tio Bob diz isso
fonte
Edição da mensagem ( reescrita completa ) Quando o pedido não importa, é quando. Ambos devem fornecer Log (n) - seria útil verificar se um é mais de cinco por cento mais rápido que o outro. O HashSet pode testar O (1) em um loop e deve revelar se é.
fonte
fonte