Comparaison HashSet et TreeSet

Comparaison de HashSet et TreeSet

1. introduction

Dans cet article, nous allons comparer deux des implémentations Java les plus populaires de l'interfacejava.util.Set -HashSet etTreeSet.

2. Différences

HashSet etTreeSet sont des feuilles de la même branche, mais elles diffèrent sur quelques points importants.

2.1. Commande

HashSet stores the objects in random order, whereas TreeSet applies the natural order of the elements. Voyons l'exemple suivant:

@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"));
}

Après avoir ajouté les objetsString dansTreeSet, nous voyons que le premier est «Génial», même s'il a été ajouté à la toute fin. Une opération similaire effectuée avecHashSet ne garantit pas que l'ordre des éléments restera constant dans le temps.

2.2. ObjetsNull

Une autre différence est 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());
}

Si nous essayons de stocker l'objetnull dans unTreeSet, l'opération se traduira par unNullPointerException lancé. La seule exception était dans Java 7 où il était autorisé à avoir exactement un élémentnull dans lesTreeSet.

2.3. Performance

En termes simples,HashSet est plus rapide que lesTreeSet.

HashSet offre des performances à temps constant pour la plupart des opérations commeadd(),remove() etcontains(), par rapport au tempslog (n) TreeSet.

Habituellement, nous pouvons voir quethe execution time for adding elements into TreeSet is much better than for the HashSet.

N'oubliez pas que la machine virtuelle Java n'est peut-être pas réchauffée et que les temps d'exécution peuvent donc différer. Une bonne discussion sur la façon de concevoir et de réaliser des micro-tests en utilisant diverses implémentations deSet est disponiblehere.

2.4. Méthodes implémentées

TreeSet is rich in functionalities, implémentant des méthodes supplémentaires telles que:

  • pollFirst() - pour renvoyer le premier élément, ounull siSet est vide

  • pollLast() - pour récupérer et supprimer le dernier élément, ou retournernull siSet est vide

  • first() - pour retourner le premier élément

  • last() pour renvoyer le dernier élément

  • ceiling() - pour renvoyer le plus petit élément supérieur ou égal à l'élément donné, ounull s'il n'y a pas de tel élément

  • lower() - pour renvoyer le plus grand élément strictement inférieur à l'élément donné, ounull s'il n'y en a pas

Les méthodes mentionnées ci-dessus rendentTreeSet beaucoup plus facile à utiliser et plus puissant queHashSet.

3. Similitudes

3.1. Éléments uniques

Les deuxTreeSet etHashSet garantissent unduplicate-free collection of elements, car il fait partie de l'interface génériqueSet:

@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. Passynchronized

None of the described Set implementations are synchronized. Cela signifie que si plusieurs threads accèdent à unSet simultanément, et qu'au moins un des threads le modifie, il doit être synchronisé en externe.

3.3. Itérateurs rapides

LesIterators renvoyés parTreeSet etHashSet sont rapides.

Cela signifie que toute modification duSet à tout moment après la création duIterator lèvera unConcurrentModificationException:

@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. Quelle implémentation utiliser?

Les deux implémentations remplissent le contrat de l'idée d'un ensemble, donc cela dépend du contexte de la mise en œuvre que nous pourrions utiliser.

Voici quelques points rapides à retenir:

  • Si nous voulons garder nos entrées triées, nous devons choisir lesTreeSet

  • Si nous accordons plus d'importance aux performances qu'à la consommation de mémoire, nous devrions choisir lesHashSet

  • Si nous manquons de mémoire, nous devrions opter pour lesTreeSet

  • Si nous voulons accéder à des éléments relativement proches les uns des autres selon leur ordre naturel, nous pourrions vouloir considérerTreeSet car il a une plus grande localité

  • Les performances deHashSet peuvent être réglées à l'aide desinitialCapacity etloadFactor, ce qui n'est pas possible pour lesTreeSet

  • Si nous voulons conserver l'ordre d'insertion et bénéficier d'un accès à temps constant, nous pouvons utiliser lesLinkedHashSet

5. Conclusion

Dans cet article, nous avons couvert les différences et les similitudes entreTreeSet etHashSet.

Comme toujours, les exemples de code pour cet article sont disponiblesover on GitHub.