Un ensemble stocke des valeurs uniques
Un HashSet (de java.util) est une collection qui conserve chaque valeur au plus une fois. Il n'y a pas de clés ni de valeurs associées comme dans un HashMap - juste un sac d'éléments distincts. Son seul rôle est de répondre vite à une question : « est-ce que cette chose est là-dedans ? »
Il y a un seul paramètre de type, <ElementType>. Comme avec ArrayList et HashMap, on type généralement la variable avec l'interface Set et on construit un HashSet.
add renvoie si la valeur était nouvelle
add ne se contente pas de stocker la valeur : il renvoie un boolean qui indique si l'ensemble a réellement changé. Ajouter une valeur déjà présente renvoie false et laisse l'ensemble intact.
Cette valeur de retour est vraiment utile : if (!seen.add(x)) { /* x est un doublon */ } vous permet de détecter les doublons en une seule ligne au fil de l'eau.
Supprimer les doublons d'une liste
Comme un ensemble refuse les répétitions, le moyen le plus rapide de dédupliquer une collection est de la verser dedans. Le constructeur de HashSet accepte n'importe quelle autre collection :
C'est la raison la plus fréquente pour laquelle les débutants se tournent vers un ensemble. Sachez simplement que vous perdez l'ordre d'origine lors de l'aller-retour - utilisez LinkedHashSet si l'ordre compte (abordé plus bas).
contains, remove et size
Les opérations courantes reprennent celles des autres collections :
Le grand avantage par rapport à un ArrayList, c'est contains. Une liste doit parcourir chaque élément pour y répondre (O(n)) ; un HashSet saute presque directement à la réponse (à peu près O(1)). Quand vous vous surprenez à appeler list.contains(...) à l'intérieur d'une boucle, c'est généralement le signal qu'il faut passer à un ensemble.
Opérations sur les ensembles : union, intersection, différence
Les ensembles brillent quand on les combine. Les méthodes se lisent presque comme du langage courant une fois qu'on sait laquelle est laquelle :
Le piège essentiel : addAll, retainAll et removeAll modifient l'ensemble sur lequel ils sont appelés. C'est pourquoi chaque exemple copie d'abord a dans un HashSet tout neuf - sinon vous détruiriez votre original. Construisez un nouvel ensemble pour chaque résultat.
HashSet ne conserve pas l'ordre
Comme HashMap, un HashSet ne garantit aucun ordre d'itération, et l'ordre peut varier d'une exécution à l'autre. Si vous avez besoin de prévisibilité :
LinkedHashSetpréserve l'ordre d'insertion - l'ordre dans lequel vous avez ajouté les éléments.TreeSetgarde les éléments triés selon leur ordre naturel (ou unComparatorque vous fournissez).
Les trois implémentent l'interface Set, donc passer de l'un à l'autre se résume à une modification d'une seule ligne dans le constructeur.
Les éléments doivent être hachables
Un HashSet repose en coulisses sur un HashMap, donc la même règle s'applique : il localise les éléments en les hachant, ce qui signifie que les méthodes hashCode() et equals() d'un élément doivent être cohérentes. Les types intégrés comme String et Integer font déjà cela correctement, c'est pourquoi les chaînes "java" en double fusionnent correctement plus haut. Si vous stockez des instances de votre propre classe, redéfinissez à la fois equals et hashCode - sinon deux objets « égaux » par leur sens seront traités comme distincts, et contains ainsi que la déduplication échoueront silencieusement.
Suite : Parcourir les collections
Vous avez désormais rencontré les trois collections incontournables - ArrayList, HashMap et HashSet. Chacune se parcourt un peu différemment, et il y a des pièges subtils (comme modifier une collection pendant qu'on boucle dessus). Ensuite, nous allons tout rassembler et voir comment parcourir proprement les collections avec la boucle for-each, les itérateurs et forEach.
Questions fréquentes
Comment créer un HashSet en Java ?
Déclarez-le avec un seul paramètre de type (le type des éléments) et appelez le constructeur : Set<String> tags = new HashSet<>();. Ajoutez des valeurs avec tags.add("java"); et testez l'appartenance avec tags.contains("java");. Importez java.util.HashSet et java.util.Set.
Quelle est la différence entre un HashSet et un ArrayList en Java ?
Un ArrayList conserve tous les éléments que vous ajoutez (doublons compris) dans l'ordre d'insertion et est indexé par position. Un HashSet ne stocke que des valeurs uniques, ne garantit aucun ordre, n'a pas d'index, et sa vérification contains est à peu près en temps constant au lieu de parcourir toute la liste. Optez pour un HashSet quand l'unicité ou l'appartenance rapide vous importe, pas la position.
Comment supprimer les doublons d'une liste en Java ?
Passez la liste au constructeur d'un HashSet : Set<String> unique = new HashSet<>(list);. L'ensemble écarte automatiquement les valeurs répétées. Si vous avez besoin de récupérer une liste (et que perdre l'ordre ne vous dérange pas), enveloppez-le à nouveau : new ArrayList<>(unique). Utilisez plutôt un LinkedHashSet si vous voulez préserver l'ordre d'origine.