CodeGym /Cours /JAVA 25 SELF /Opérations union, intersection, difference

Opérations union, intersection, difference

JAVA 25 SELF
Niveau 32 , Leçon 2
Disponible

1. Opération union (union d’ensembles)

En Java, l’interface Set<T> est utilisée pour représenter des ensembles. À la différence des listes (List), les ensembles garantissent l’unicité des éléments et, en général, ne se soucient pas de l’ordre (sauf si vous utilisez des implémentations spécifiques, par exemple LinkedHashSet). Les implémentations les plus populaires — HashSet et TreeSet. Leur objectif principal — déterminer rapidement la présence d’un élément et garantir l’absence de doublons.

Quand utiliser des ensembles ?

  • Quand l’unicité est importante : par exemple, la liste de tous les utilisateurs uniques ayant visité le site.
  • Quand vous devez vérifier rapidement la présence d’un élément : la méthode contains de HashSet fonctionne généralement en temps constant.
  • Quand vous devez effectuer des opérations classiques sur des ensembles : union, intersection, différence.

Union — c’est la réunion de deux ensembles ou plus : le résultat contient tous les éléments des ensembles d’origine (sans doublons).

Exemple pratique

Supposons que nous ayons deux ensembles d’étudiants qui fréquentent les clubs « Robotique » et « Programmation » :

Set<String> robotics = Set.of("Alice", "Boris", "Victoria");
Set<String> programming = Set.of("Victoria", "George", "Diana");

Nous devons obtenir l’ensemble de tous les étudiants qui vont à au moins un des clubs.

Solution via Stream API

La manière la plus directe est de concaténer les deux flux et de les collecter dans un Set :

Set<String> all = Stream.concat(
        robotics.stream(),
        programming.stream()
    ).collect(Collectors.toSet());

System.out.println(all); // [Alice, Boris, Victoria, George, Diana]

Explications :

  • Stream.concat concatène deux flux.
  • collect(Collectors.toSet()) collecte les éléments dans un ensemble (en supprimant automatiquement les doublons).

Alternative : plus de deux ensembles

S’il y a trois clubs ou plus, utilisez Stream.of et flatMap :

Set<String> math = Set.of("Eugene", "Victoria", "Boris");

Set<String> all = Stream.of(robotics, programming, math)
    .flatMap(Set::stream)
    .collect(Collectors.toSet());

System.out.println(all); // [Alice, Boris, Victoria, George, Diana, Eugene]

Pourquoi Set ?
Parce que Set supprime automatiquement les doublons. Si vous collectez dans une List, les mêmes noms apparaîtront plusieurs fois.

2. Opération intersection (intersection des ensembles)

Intersection — ce sont les éléments présents simultanément dans les deux ensembles.

Exemple pratique

Trouver les étudiants qui vont à la fois en « Robotique » et en « Programmation » :

Set<String> robotics = Set.of("Alice", "Boris", "Victoria");
Set<String> programming = Set.of("Victoria", "George", "Diana");

Solution via Stream API :

Set<String> both = robotics.stream()
    .filter(programming::contains)
    .collect(Collectors.toSet());

System.out.println(both); // [Victoria]

Explications :
Nous parcourons tous les membres de « Robotique » et ne gardons que ceux qui figurent dans « Programmation ». Le résultat est un ensemble de noms présents dans les deux clubs.

Méthode alternative (sans Stream API)

Vous pouvez utiliser la méthode intégrée retainAll (elle modifie l’ensemble courant) :

Set<String> intersection = new HashSet<>(robotics);
intersection.retainAll(programming);
System.out.println(intersection); // [Victoria]

Mais dans le cadre de ce cours sur le Stream API, nous mettons l’accent sur les streams.

3. Opération difference (différence des ensembles)

Difference — ce sont les éléments du premier ensemble qui n’appartiennent pas au second.

Exemple pratique

Trouver les étudiants qui vont uniquement en « Robotique », mais pas en « Programmation » :

Set<String> robotics = Set.of("Alice", "Boris", "Victoria");
Set<String> programming = Set.of("Victoria", "George", "Diana");

Solution via Stream API :

Set<String> onlyRobotics = robotics.stream()
    .filter(name -> !programming.contains(name))
    .collect(Collectors.toSet());

System.out.println(onlyRobotics); // [Alice, Boris]

Explications :
Nous filtrons les membres de « Robotique » en ne gardant que ceux qui ne figurent pas dans « Programmation ».

Méthode alternative (sans Stream API)

Set<String> difference = new HashSet<>(robotics);
difference.removeAll(programming);
System.out.println(difference); // [Alice, Boris]

4. Exercices pratiques : traitement de listes d’utilisateurs

Exercice 1 : trouver les étudiants qui ne vont qu’à un seul club

Nous voulons savoir qui va uniquement à « Robotique » ou uniquement à « Programmation », mais pas aux deux à la fois. C’est la différence symétrique (xor pour les ensembles) :

Set<String> onlyOne = Stream.concat(
        robotics.stream().filter(name -> !programming.contains(name)),
        programming.stream().filter(name -> !robotics.contains(name))
    ).collect(Collectors.toSet());

System.out.println(onlyOne); // [Alice, Boris, George, Diana]

Exercice 2 : liste de tous les étudiants uniques provenant de plusieurs clubs

Set<String> all = Stream.of(robotics, programming, math)
    .flatMap(Set::stream)
    .collect(Collectors.toSet());

System.out.println(all); // [Alice, Boris, Victoria, George, Diana, Eugene]

Exercice 3 : trouver les étudiants qui ne vont dans aucun club

Supposons que nous ayons la liste de tous les élèves de la classe :

Set<String> allStudents = Set.of("Alice", "Boris", "Victoria", "George", "Diana", "Eugene", "Ian", "Kate");

Nous devons déterminer qui ne fréquente aucun club :

Set<String> attendees = Stream.of(robotics, programming, math)
    .flatMap(Set::stream)
    .collect(Collectors.toSet());

Set<String> notInAny = allStudents.stream()
    .filter(name -> !attendees.contains(name))
    .collect(Collectors.toSet());

System.out.println(notInAny); // [Ian, Kate]

5. Remarques importantes : equals, hashCode et efficacité

Pourquoi est-il important d’implémenter correctement equals et hashCode ?

Toutes les opérations sur les ensembles (Set) dépendent de la correction des méthodes equals et hashCode. Si vous stockez des objets de votre propre classe (par exemple Student), il est indispensable de redéfinir ces méthodes, sinon les comparaisons fonctionneront de manière incorrecte.

Exemple :

class Student {
    String name;
    int age;

    // N'oubliez pas de redéfinir equals et hashCode !
}

Si vous ne le faites pas, deux étudiants avec le même nom et le même âge seront considérés comme des objets différents pour un Set.

Pourquoi vaut-il mieux utiliser Set plutôt que List ?

  • L’opération contains sur un Set est rapide (généralement en temps constant).
  • Dans une List, la recherche d’un élément est en temps linéaire, ce qui peut être critique pour de grandes collections.
  • Pour les opérations sur les ensembles (union, intersection, difference), un Set est bien plus efficace et pertinent.

6. Erreurs typiques lors de l’utilisation des opérations sur les ensembles

Erreur n°1 : utiliser List au lieu de Set pour des opérations sur des ensembles. Si vous collectez les éléments dans une List, les doublons ne sont pas supprimés et l’opération contains est lente. Pour union/intersection/difference, utilisez Set.

Erreur n°2 : equals/hashCode non implémentés pour les objets. Si vous stockez dans un Set des objets de votre propre classe, mais que vous n’avez pas redéfini les méthodes equals et hashCode, l’intersection et la différence se comporteront de façon « étrange » — des objets qui sont identiques sur le plan sémantique ne seront pas considérés comme égaux.

Erreur n°3 : modification de la collection pendant un stream. Si vous essayez de modifier le Set source directement dans un stream (par exemple, en ajoutant ou supprimant des éléments), vous obtiendrez une ConcurrentModificationException. Travaillez toujours avec un nouvel ensemble.

Erreur n°4 : perte d’ordre non évidente. HashSet ne garantit pas l’ordre des éléments. Si l’ordre est important, utilisez LinkedHashSet ou TreeSet.

Erreur n°5 : utiliser Stream.concat pour plus de deux collections. Stream.concat concatène seulement deux flux. Pour un nombre supérieur, utilisez Stream.of(...) et flatMap.

Erreur n°6 : problèmes avec null. Les ensembles n’aiment pas les valeurs null, surtout si vous utilisez Set.of(...) — il n’accepte pas null. Pour gérer null, utilisez d’autres implémentations ou filtrez les valeurs en amont.

1
Mission
JAVA 25 SELF, niveau 32, leçon 2
Bloqué
Identification des suspects uniques provenant de différentes sources 🕵️‍♂️
Identification des suspects uniques provenant de différentes sources 🕵️‍♂️
1
Mission
JAVA 25 SELF, niveau 32, leçon 2
Bloqué
Catalogage des artefacts de différentes fouilles archéologiques 🏺
Catalogage des artefacts de différentes fouilles archéologiques 🏺
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION