Java Set expliqué : guide complet des collections uniques, HashSet, LinkedHashSet et TreeSet

1. Qu’est‑ce qu’un Set ?

En programmation Java, un Set est l’un des types de collection les plus importants. Le mot « Set » vient des mathématiques et, à l’instar d’un ensemble mathématique, il possède la caractéristique clé qu’il ne peut pas contenir d’éléments dupliqués.
Un Set est utilisé lorsque vous ne souhaitez gérer que des valeurs uniques, quel que soit le type de données (nombres, chaînes ou objets).

Quelle est la différence entre Set et List ?

Le Java Collections Framework propose plusieurs structures de données telles que List et Map. Parmi elles, Set et List sont souvent comparés. Leurs principales différences sont les suivantes :

  • List : Autorise les valeurs dupliquées et préserve l’ordre des éléments (basé sur l’index).
  • Set : N’autorise pas les duplications, et l’ordre des éléments n’est pas garanti (sauf pour certaines implémentations).

En bref, une List est une « collection ordonnée », tandis qu’un Set est une « collection d’éléments uniques ».
Par exemple, si vous devez gérer des identifiants d’utilisateurs sans duplication, un Set est le choix idéal.

Avantages d’utiliser un Set

  • Élimination automatique des duplicatas Même lorsqu’on reçoit une grande quantité de données provenant des utilisateurs, le simple fait d’ajouter des éléments à un Set garantit que les duplicatas ne sont stockés qu’une seule fois. Cela supprime le besoin de vérifications manuelles et simplifie l’implémentation.
  • Recherche et suppression efficaces Les Sets sont conçus pour effectuer rapidement des vérifications d’existence et des opérations de suppression, bien que les performances varient selon l’implémentation (par ex. HashSet ou TreeSet).

Quand faut‑il utiliser un Set ?

  • Lors de la gestion d’informations qui ne doivent pas être dupliquées, comme les adresses e‑mail ou les identifiants d’utilisateurs
  • Lorsque l’unicité des données doit être garantie
  • Quand vous souhaitez créer efficacement une liste de valeurs uniques à partir d’un grand jeu de données

Comme indiqué ci‑dessus, le Set est le mécanisme standard en Java pour gérer intelligemment les collections qui n’autorisent pas les duplications.
Dans les sections suivantes, nous explorerons en détail les spécifications du Set, les modèles d’utilisation et des exemples de code concrets.

2. Spécifications de base et avantages du Set

En Java, le Set est défini par l’interface java.util.Set. En implémentant cette interface, vous pouvez représenter une collection d’éléments uniques sans duplicatas. Examinons de plus près les spécifications essentielles et les avantages du Set.

Caractéristiques de base de l’interface Set

Un Set possède les caractéristiques suivantes :

  • Pas d’éléments dupliqués : si vous essayez d’ajouter un élément déjà présent, il ne sera pas ajouté. Par exemple, même si vous exécutez set.add("apple") deux fois, seul un « apple » sera stocké.
  • L’ordre n’est pas garanti (dépend de l’implémentation) : un Set ne garantit pas l’ordre des éléments par défaut. Cependant, certaines implémentations comme LinkedHashSet et TreeSet gèrent les éléments dans un ordre spécifique.
  • Gestion des éléments null : la prise en charge de null dépend de l’implémentation. Par exemple, HashSet autorise un seul élément null, tandis que TreeSet n’en accepte aucun.

Importance de equals et hashCode

Le fait que deux éléments soient considérés comme des duplicatas dans un Set est déterminé par les méthodes equals et hashCode.
Lorsque vous utilisez des classes personnalisées comme éléments d’un Set, ne pas surcharger correctement ces méthodes peut entraîner des duplicatas inattendus ou un comportement de stockage incorrect.

  • equals : détermine si deux objets sont logiquement égaux
  • hashCode : renvoie une valeur numérique utilisée pour une identification efficace

Avantages d’utiliser un Set

Les Sets offrent plusieurs avantages pratiques :

  • Élimination facile des duplicatas : le simple fait d’ajouter des valeurs à un Set garantit que les duplicatas sont automatiquement supprimés, éliminant ainsi le besoin de vérifications manuelles.
  • Recherche et suppression efficaces : des implémentations comme HashSet offrent des opérations de recherche et de suppression rapides, souvent plus performantes que les Lists.
  • API simple et intuitive : les méthodes de base comme add, remove et contains rendent les Sets faciles à utiliser.

Implémentation interne et performances

L’une des implémentations de Set les plus courantes, HashSet, utilise en interne un HashMap pour gérer les éléments. Cela permet d’ajouter, de supprimer et de rechercher des éléments avec une complexité moyenne de O(1).
Si un ordre ou un tri est requis, vous pouvez choisir des implémentations telles que LinkedHashSet ou TreeSet selon vos besoins.

3. Principales classes d’implémentation et leurs caractéristiques

Java propose plusieurs implémentations majeures de l’interface Set. Chacune possède des caractéristiques différentes, il est donc important de choisir la bonne selon votre cas d’utilisation.
Ici, nous expliquerons les trois implémentations les plus couramment utilisées : HashSet, LinkedHashSet et TreeSet.

HashSet

HashSet est l’implémentation de Set la plus couramment utilisée.

  • Caractéristiques
  • Ne préserve pas l’ordre des éléments (l’ordre d’insertion et l’ordre d’itération peuvent différer).
  • Utilise en interne un HashMap, offrant des opérations d’ajout, de recherche et de suppression rapides.
  • Autorise un élément null.
  • Cas d’utilisation typiques
  • Idéal lorsque vous souhaitez éliminer les doublons et que l’ordre n’a pas d’importance.
  • Exemple de code
    Set<String> set = new HashSet<>();
    set.add("apple");
    set.add("banana");
    set.add("apple"); // Duplicate is ignored
    
    for (String s : set) {
        System.out.println(s); // Only "apple" and "banana" are printed
    }
    

LinkedHashSet

LinkedHashSet étend la fonctionnalité de HashSet en préservant l’ordre d’insertion.

  • Caractéristiques
  • Les éléments sont itérés dans l’ordre dans lequel ils ont été insérés.
  • Géré en interne à l’aide d’une combinaison d’une table de hachage et d’une liste chaînée.
  • Légèrement plus lent que HashSet, mais utile lorsque l’ordre est important.
  • Cas d’utilisation typiques
  • Idéal lorsque vous souhaitez supprimer les doublons tout en conservant l’ordre d’insertion.
  • Exemple de code
    Set<String> set = new LinkedHashSet<>();
    set.add("apple");
    set.add("banana");
    set.add("orange");
    
    for (String s : set) {
        System.out.println(s); // Printed in order: apple, banana, orange
    }
    

TreeSet

TreeSet est une implémentation de Set qui trie automatiquement les éléments.

  • Caractéristiques
  • Utilise en interne un arbre rouge-noir (une structure d’arbre équilibré).
  • Les éléments sont automatiquement triés par ordre croissant.
  • Un ordre personnalisé est possible en utilisant Comparable ou Comparator.
  • Les valeurs null ne sont pas autorisées.
  • Cas d’utilisation typiques
  • Utile lorsque vous avez besoin à la fois d’unicité et de tri automatique.
  • Exemple de code
    Set<Integer> set = new TreeSet<>();
    set.add(30);
    set.add(10);
    set.add(20);
    
    for (Integer n : set) {
        System.out.println(n); // Printed in order: 10, 20, 30
    }
    

Résumé

  • HashSet : Idéal pour des performances élevées lorsque l’ordre n’est pas requis
  • LinkedHashSet : À utiliser lorsque l’ordre d’insertion est important
  • TreeSet : À utiliser lorsque le tri automatique est requis

Le choix de la bonne implémentation de Set dépend de vos exigences spécifiques. Sélectionnez celle qui convient le mieux et utilisez‑la efficacement.

4. Méthodes courantes et comment les utiliser

L’interface Set fournit diverses méthodes pour les opérations de collection. Vous trouverez ci‑dessous les méthodes les plus couramment utilisées, expliquées avec des exemples.

Méthodes principales

  • add(E e) Ajoute un élément au Set. Si l’élément existe déjà, il n’est pas ajouté.
  • remove(Object o) Supprime l’élément spécifié du Set. Retourne true si l’opération réussit.
  • contains(Object o) Vérifie si le Set contient l’élément spécifié.
  • size() Retourne le nombre d’éléments dans le Set.
  • clear() Supprime tous les éléments du Set.
  • isEmpty() Vérifie si le Set est vide.
  • iterator() Retourne un Iterator pour parcourir les éléments.
  • toArray() Convertit le Set en tableau.

Exemple d’utilisation de base

Set<String> set = new HashSet<>();

// Add elements
set.add("apple");
set.add("banana");
set.add("apple"); // Duplicate ignored

// Get size
System.out.println(set.size()); // 2

// Check existence
System.out.println(set.contains("banana")); // true

java // Supprimer l’élément set.remove(« banana »); System.out.println(set.contains(« banana »)); // faux

// Effacer tous les éléments set.clear(); System.out.println(set.isEmpty()); // vrai

### Iterating Over a Set



Since Set does not support index-based access (e.g., set.get(0)), use an Iterator or enhanced for-loop.

// Boucle for améliorée Set set = new HashSet<>(); set.add(« A »); set.add(« B »); set.add(« C »);

for (String s : set) { System.out.println(s); }


// Utilisation de l’itérateur Iterator it = set.iterator(); while (it.hasNext()) { String s = it.next(); System.out.println(s); }

### Important Notes



* Adding an existing element using `add` does not change the Set.
* Element order depends on the implementation (HashSet: unordered, LinkedHashSet: insertion order, TreeSet: sorted).



## 5. Common Use Cases and Typical Scenarios



Java Sets are widely used in many situations where **duplicate values must be avoided**. Below are some of the most common and practical use cases encountered in real-world development.



### Creating a Unique List (Duplicate Removal)



When you want to **extract only unique values from a large dataset**, Set is extremely useful.  
For example, it can automatically remove duplicates from user input or existing collections.



**Example: Creating a Set from a List to Remove Duplicates**

List list = Arrays.asList(« apple », « banana », « apple », « orange »); Set set = new HashSet<>(list);

System.out.println(set); // [apple, banana, orange]






### Ensuring Input Uniqueness



Sets are ideal for scenarios where **duplicate values must not be registered**, such as user IDs or email addresses.  
You can immediately determine whether a value already exists by checking the return value of `add`.

Set emailSet = new HashSet<>(); boolean added = emailSet.add(« user@example.com »); if (!added) { System.out.println(« Cette valeur est déjà enregistrée »); }

### Storing Custom Classes and Implementing equals/hashCode



When storing custom objects in a Set, proper implementation of **equals** and **hashCode** is essential.  
Without them, objects with the same logical content may be treated as different elements.



**Example: Ensuring Uniqueness in a Person Class**

class Person { String name;

Person(String name) {
    this.name = name;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    Person person = (Person) obj;
    return Objects.equals(name, person.name);
}

@Override
public int hashCode() {
    return Objects.hash(name);
}

}

// Exemple d’utilisation Set people = new HashSet<>(); people.add(new Person(« Taro »)); people.add(new Person(« Taro »)); // Sans implémentation correcte, des doublons peuvent survenir System.out.println(people.size()); // 1

### Fast Lookup and Data Filtering



Because Set provides fast lookups via `contains`, it is often used for filtering and comparison tasks.  
Converting a List to a Set can significantly improve performance when repeatedly checking for existence.



**Example: Fast Keyword Lookup**

Set keywordSet = new HashSet<>(Arrays.asList(« java », « python », « c »)); boolean found = keywordSet.contains(« python »); // vrai

## 6. Performance Considerations and Pitfalls



While Set is a powerful collection for managing unique elements, improper usage can lead to unexpected behavior or performance issues. This section explains key performance characteristics and common pitfalls.



### Performance Differences by Implementation
  • HashSet utilise une table de hachage en interne, offrant des performances moyennes O(1) pour les opérations d’ajout, de suppression et de recherche. Les performances peuvent se dégrader si le nombre d’éléments devient extrêmement grand ou si les collisions de hachage sont fréquentes.
  • LinkedHashSet performances similaires à HashSet, mais avec une surcharge supplémentaire due au maintien de l’ordre d’insertion. Dans la plupart des cas, la différence est négligeable sauf lors du traitement de très grands ensembles de données.
  • TreeSet utilise un arbre rouge‑noir en interne, ce qui donne des performances O(log n) pour les opérations d’ajout, de suppression et de recherche. Plus lent que HashSet, mais fournit un tri automatique.

Utilisation d’objets mutables comme éléments d’un Set

Une prudence supplémentaire est requise lors du stockage d’objets mutables dans un Set.
HashSet et TreeSet s’appuient sur les valeurs hashCode ou compareTo pour gérer les éléments.
Si ces valeurs changent après l’insertion, la recherche et la suppression peuvent échouer.

Exemple : Piège avec des objets mutables

Set<Person> people = new HashSet<>();
Person p = new Person("Taro");
people.add(p);

p.name = "Jiro"; // Modifying after insertion
people.contains(p); // May return false unexpectedly

Pour éviter de tels problèmes, il est fortement recommandé d’utiliser des objets immuables comme éléments d’un Set chaque fois que possible.

Gestion des valeurs null

  • HashSet / LinkedHashSet : Autorise un seul élément null
  • TreeSet : N’autorise pas null (lance NullPointerException)

Autres notes importantes

  • Modification pendant l’itération Modifier un Set pendant son itération peut provoquer une ConcurrentModificationException. Utilisez Iterator.remove() au lieu de modifier directement le Set.
  • Choisir la bonne implémentation Utilisez LinkedHashSet ou TreeSet lorsque l’ordre est important. HashSet ne garantit pas l’ordre.

7. Tableau comparatif (Vue d’ensemble)

Le tableau ci-dessous résume les différences entre les principales implémentations de Set pour une comparaison facile.

ImplementationNo DuplicatesOrder PreservedSortedPerformancenull AllowedTypical Use Case
HashSetYesNoNoFast (O(1))One allowedDuplicate removal, order not required
LinkedHashSetYesYes (Insertion order)NoSlightly slower than HashSetOne allowedDuplicate removal with order preservation
TreeSetYesNoYes (Automatic)O(log n)Not allowedDuplicate removal with sorting

Points clés à retenir

  • HashSet : Le choix par défaut lorsque l’ordre n’est pas pertinent et que les performances sont critiques.
  • LinkedHashSet : Le meilleur choix lorsque l’ordre d’insertion doit être préservé.
  • TreeSet : Idéal lorsque le tri automatique est requis.

8. Questions fréquemment posées (FAQ)

Q1. Les types primitifs (int, char, etc.) peuvent-ils être utilisés dans un Set ?

A1. Non. Utilisez des classes enveloppantes comme Integer ou Character à la place.

Q2. Que se passe-t-il si la même valeur est ajoutée plusieurs fois ?

A2. Seule la première insertion est conservée. La méthode add renvoie false si l’élément existe déjà.

Q3. Quand devrais-je utiliser List vs Set ?

A3. Utilisez List lorsque l’ordre ou les doublons sont importants, et Set lorsque l’unicité est requise.

Q4. Qu’est-ce qui est requis pour stocker des objets personnalisés dans un Set ?

A4. Surcharger correctement equals et hashCode.

Q5. Comment puis-je préserver l’ordre d’insertion ?

A5. Utilisez LinkedHashSet.

Q6. Comment puis-je trier les éléments automatiquement ?

A6. Utilisez TreeSet.

Q7. Un Set peut-il contenir des valeurs null ?

A7. HashSet et LinkedHashSet autorisent un null ; TreeSet n’en autorise pas.

Q8. Comment obtenir la taille d’un Set ?

A8. Utilisez size().

Q9. Comment convertir un Set en List ou en tableau ?

A9.

  • En tableau : toArray()
  • En List : new ArrayList<>(set)

Q10. Puis-je supprimer des éléments pendant l’itération ?

A10. Oui, mais uniquement en utilisant Iterator.remove().

9. Conclusion

Cet article a couvert les collections Set de Java, des fondamentaux à l’utilisation avancée. Les points clés incluent :

  • Set est conçu pour gérer des collections d’éléments uniques, ce qui le rend idéal pour l’élimination des doublons.
  • Les principales implémentations comprennent HashSet (rapide, non ordonné), LinkedHashSet (ordre d’insertion) et TreeSet (trié).
  • Les cas d’utilisation courants incluent la suppression de doublons, les vérifications d’unicité, la gestion d’objets personnalisés et les recherches rapides.
  • Comprendre les caractéristiques de performance et les pièges tels que les objets mutables et les règles d’itération est essentiel.
  • Le tableau comparatif et la FAQ offrent des conseils pratiques pour le développement en conditions réelles.

Maîtriser les collections Set rend la programmation Java plus propre, plus sûre et plus efficace.
Ensuite, envisagez de combiner les Sets avec les Listes ou les Maps pour créer des structures de données et des solutions plus avancées.