Comparação HashSet e TreeSet

Comparação HashSet e TreeSet

1. Introdução

Neste artigo, vamos comparar duas das implementações Java mais populares da interfacejava.util.Set -HashSeteTreeSet.

2. Diferenças

HashSeteTreeSet são folhas do mesmo ramo, mas diferem em alguns assuntos importantes.

2.1. Encomenda

HashSet stores the objects in random order, whereas TreeSet applies the natural order of the elements. Vejamos o seguinte exemplo:

@Test
public void givenTreeSet_whenRetrievesObjects_thenNaturalOrder() {
    Set set = new TreeSet<>();
    set.add("example");
    set.add("is");
    set.add("Awesome");

    assertEquals(3, set.size());
    assertTrue(set.iterator().next().equals("Awesome"));
}

Depois de adicionar os objetosString emTreeSet, vemos que o primeiro é “Incrível”, embora tenha sido adicionado no final. Uma operação semelhante feita comHashSet não garante que a ordem dos elementos permanecerá constante ao longo do tempo.

2.2. ObjetosNull

Outra diferença é queHashSet can store null objects, while TreeSet does not allow them:

@Test(expected = NullPointerException.class)
public void givenTreeSet_whenAddNullObject_thenNullPointer() {
    Set set = new TreeSet<>();
    set.add("example");
    set.add("is");
    set.add(null);
}

@Test
public void givenHashSet_whenAddNullObject_thenOK() {
    Set set = new HashSet<>();
    set.add("example");
    set.add("is");
    set.add(null);

    assertEquals(3, set.size());
}

Se tentarmos armazenar o objetonull em umTreeSet, a operação resultará em umNullPointerException lançado. A única exceção era em Java 7, quando era permitido ter exatamente um elementonull emTreeSet.

2.3. atuação

Simplificando,HashSet é mais rápido do queTreeSet.

HashSet fornece desempenho de tempo constante para a maioria das operações comoadd(),remove()econtains(), versus o tempolog (n) oferecido pelo TreeSet.

Normalmente, podemos ver quethe execution time for adding elements into TreeSet is much better than for the HashSet.

Lembre-se de que a JVM pode não estar aquecida, portanto os tempos de execução podem ser diferentes. Uma boa discussão sobre como projetar e executar microtestes usando várias implementações deSet está disponívelhere.

2.4. Métodos Implementados

TreeSet is rich in functionalities, implementando métodos adicionais como:

  • pollFirst() - para retornar o primeiro elemento, ounull seSet estiver vazio

  • pollLast() - para recuperar e remover o último elemento ou retornarnull seSet estiver vazio

  • first() - para devolver o primeiro item

  • last() para devolver o último item

  • ceiling() - para retornar o menor elemento maior ou igual ao elemento dado, ounull se não houver tal elemento

  • lower() - para retornar o maior elemento estritamente menor do que o elemento dado, ounull se não houver tal elemento

Os métodos mencionados acima tornamTreeSet muito mais fácil de usar e mais poderoso do queHashSet.

3. Semelhanças

3.1. Elementos Únicos

AmbosTreeSeteHashSet garantem umduplicate-free collection of elements,, pois é parte da interfaceSet genérica:

@Test
public void givenHashSetAndTreeSet_whenAddDuplicates_thenOnlyUnique() {
    Set set = new HashSet<>();
    set.add("example");
    set.add("example");

    assertTrue(set.size() == 1);

    Set set2 = new TreeSet<>();
    set2.add("example");
    set2.add("example");

    assertTrue(set2.size() == 1);
}

3.2. Nãosynchronized

None of the described Set implementations are synchronized. Isso significa que se vários threads acessam umSet simultaneamente e pelo menos um dos threads o modifica, então ele deve ser sincronizado externamente.

3.3. Iteradores Fail-Fast

OIterators retornado porTreeSet eHashSet são fail-fast.

Isso significa que qualquer modificação deSet a qualquer momento após a criação deIterator lançará umConcurrentModificationException:

@Test(expected = ConcurrentModificationException.class)
public void givenHashSet_whenModifyWhenIterator_thenFailFast() {
    Set set = new HashSet<>();
    set.add("example");
    Iterator it = set.iterator();

    while (it.hasNext()) {
        set.add("Awesome");
        it.next();
    }
}

4. Qual implementação usar?

Ambas as implementações cumprem o contrato da ideia de um conjunto, portanto, depende do contexto que implementação podemos usar.

Aqui estão alguns pontos rápidos a serem lembrados:

  • Se quisermos manter nossas entradas classificadas, precisamos ir para oTreeSet

  • Se valorizamos o desempenho mais do que o consumo de memória, devemos ir para oHashSet

  • Se temos pouca memória, devemos ir para oTreeSet

  • Se quisermos acessar elementos que são relativamente próximos uns dos outros de acordo com sua ordem natural, podemos considerarTreeSet porque tem maior localidade

  • O desempenho deHashSet pode ser ajustado usandoinitialCapacityeloadFactor, o que não é possível paraTreeSet

  • Se quisermos preservar o pedido de inserção e aproveitar o acesso em tempo constante, podemos usar oLinkedHashSet

5. Conclusão

Neste artigo, cobrimos as diferenças e semelhanças entreTreeSet eHashSet.

Como sempre, os exemplos de código para este artigo estão disponíveisover on GitHub.