CodeGym /Cours /JAVA 25 SELF /Méthodes join, concat: fusion des flux

Méthodes join, concat: fusion des flux

JAVA 25 SELF
Niveau 32, Leçon 1
Disponible

1. Stream.concat: fusionner deux flux

En programmation, on rencontre souvent la situation suivante: nous avons deux (ou plus) flux de données et nous voulons les fusionner en un seul. Par exemple, deux listes d’étudiants de groupes différents — et il faut tous les traiter d’un coup. À l’époque (avant le Stream API), nous aurions simplement fusionné deux listes avec addAll. Mais si nous travaillons avec des flux (Stream<T>), nous voulons le faire de manière paresseuse et expressive.

Syntaxe et principe de fonctionnement

La manière la plus basique de fusionner deux flux est d’utiliser la méthode statique Stream.concat:

Stream<T> Stream.concat(Stream<? extends T> a, Stream<? extends T> b)

Ce qui se passe:

  • On prend d’abord le premier flux, puis le second.
  • En sortie — un nouveau flux qui émet d’abord tous les éléments du premier, puis tous ceux du second.
  • La fusion est paresseuse: tant que vous ne parcourez pas le flux final avec une opération terminale (par exemple, forEach ou collect), rien ne se passe.

Exemple: fusion de deux listes de prénoms

import java.util.List;
import java.util.stream.Stream;

List<String> groupA = List.of("Anna", "Boris", "Victoria");
List<String> groupB = List.of("George", "Daria");

Stream<String> allStudents = Stream.concat(groupA.stream(), groupB.stream());

allStudents.forEach(System.out::println);

Résultat:

Anna
Boris
Victoria
George
Daria

Important: après la fusion, le flux résultant est à usage unique. Comme tout autre Stream, il ne peut pas être réutilisé.

Particularités de Stream.concat

  • Uniquement deux flux à la fois. Pour trois et plus — soit en chaînant concat, soit avec d’autres approches (voir ci-dessous).
  • L’ordre est conservé. D’abord les éléments du premier flux, puis ceux du second.
  • Fusion paresseuse. Si le premier flux est infini, on n’atteindra jamais le second.
  • Erreur typique: essayer de fusionner un flux avec lui-même (Stream.concat(stream, stream)) revient à réutiliser le même flux — c’est interdit.

2. Fusion de plus de deux flux: flatMap et Stream.of

Quand il y a plus de deux flux, il est plus pratique de les rassembler grâce à la combinaison Stream.of + flatMap.

Utiliser Stream.of + flatMap

import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;

List<String> groupA = List.of("Anna", "Boris");
List<String> groupB = List.of("Victoria");
List<String> groupC = List.of("George", "Daria");

Stream<String> allStudents = Stream.of(groupA, groupB, groupC)
    .flatMap(Collection::stream);

allStudents.forEach(System.out::println);

Ce qui se passe ici:

  • Stream.of(groupA, groupB, groupC) crée un flux de trois collections.
  • flatMap(Collection::stream) déplie chaque collection en un flux unique de chaînes.
  • Au final — un grand flux de tous les étudiants.

Avantage: vous pouvez fusionner autant de flux que nécessaire.

Autre variante: fusionner une liste de flux

import java.util.List;
import java.util.stream.Stream;

List<Stream<String>> streams = List.of(
    Stream.of("a", "b"),
    Stream.of("c"),
    Stream.of("d", "e")
);

Stream<String> merged = streams.stream()
    .flatMap(s -> s);

merged.forEach(System.out::println);

Résultat:

a
b
c
d
e

3. Collectors.joining: concaténer des chaînes avec un séparateur

Parfois, il ne faut pas seulement fusionner des flux, mais coller tous les éléments en une seule chaîne — par exemple, pour afficher des prénoms séparés par des virgules. Pour cela, il existe le collecteur Collectors.joining.

Syntaxe

String result = stream.collect(Collectors.joining(", "));
  • Sans arguments: concatène les chaînes sans séparateur.
  • Avec séparateur: place la chaîne indiquée entre les éléments.
  • Avec préfixe et suffixe: Collectors.joining(delimiter, prefix, suffix)

Exemple: liste des étudiants en une seule chaîne

import java.util.List;
import java.util.stream.Collectors;

List<String> students = List.of("Anna", "Boris", "Victoria");

String line = students.stream()
    .collect(Collectors.joining(", "));

System.out.println("Liste des étudiants: " + line);

Résultat:

Liste des étudiants: Anna, Boris, Victoria

Exemple avec préfixe et suffixe

String line = students.stream()
    .collect(Collectors.joining(", ", "[", "]"));

System.out.println(line);

Résultat:

[Anna, Boris, Victoria]

À quoi ça sert ?

  • Génération de lignes CSV.
  • KBel affichage de listes et de rapports.
  • Transmission de données sous forme d’une seule chaîne (par exemple, dans une URL ou un log).

4. Comparaison de concat et flatMap: quand utiliser quoi ?

  • Stream.concat — quand il y a exactement deux flux et qu’il est important de conserver l’ordre.
  • Stream.of + flatMap — quand il y a beaucoup de flux ou qu’ils sont stockés dans une collection.
  • Collectors.joining — quand les éléments sont de type String et que vous avez besoin d’un résultat sous forme d’une seule chaîne.

Tableau comparatif

Méthode Quand l’utiliser Exemple de code
Stream.concat(a, b)
Deux flux
Stream.concat(a, b)
Stream.of(...).flatMap()
De nombreux flux, collection de flux
Stream.of(a, b, c).flatMap(x -> x)
Collectors.joining()
Assembler les éléments en une seule chaîne
stream.collect(Collectors.joining(", "))

5. Exemples pratiques dans une application pédagogique

Exemple 1. Fusionner des étudiants de deux facultés

import java.util.List;
import java.util.stream.Stream;

List<String> itStudents = List.of("Anna", "Boris");
List<String> mathStudents = List.of("Victoria", "George");

Stream<String> all = Stream.concat(itStudents.stream(), mathStudents.stream());

all.forEach(System.out::println);

Exemple 2. Fusionner des étudiants de tous les groupes, afficher sur une seule ligne

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

List<List<String>> allGroups = List.of(
    List.of("Anna", "Boris"),
    List.of("Victoria"),
    List.of("George", "Daria")
);

String allNames = allGroups.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.joining("; "));

System.out.println("Tous les étudiants: " + allNames);

Résultat:

Tous les étudiants: Anna; Boris; Victoria; George; Daria

Exemple 3. Fusionner des flux de nombres

import java.util.stream.Stream;

Stream<Integer> s1 = Stream.of(1, 2, 3);
Stream<Integer> s2 = Stream.of(4, 5);

Stream<Integer> merged = Stream.concat(s1, s2);

merged.forEach(System.out::print); // 12345

6. Points importants et particularités

concat ne fonctionne qu’avec deux flux

Pour trois flux, on obtient un enchaînement imbriqué — cela fonctionne, mais c’est verbeux. Il vaut mieux utiliser Stream.of + flatMap:

Stream<Integer> s1 = Stream.of(1);
Stream<Integer> s2 = Stream.of(2);
Stream<Integer> s3 = Stream.of(3);

Stream<Integer> merged = Stream.concat(Stream.concat(s1, s2), s3);

Les flux sont à usage unique

Après une opération terminale, un flux ne peut plus être utilisé. Une réutilisation entraîne une IllegalStateException.

Le flux concat est paresseux

Si le premier flux est infini (par exemple, créé via Stream.generate(...)), on n’atteindra jamais le second.

Ordre des éléments

L’ordre est toujours conservé: d’abord le premier flux, puis le second.

Collectors.joining ne fonctionne qu’avec Stream<String>

Pour des types non chaînes, convertissez d’abord les éléments en chaînes, par exemple via map(Object::toString):

import java.util.stream.Collectors;
import java.util.stream.Stream;

Stream<Integer> numbers = Stream.of(1, 2, 3);
String line = numbers
    .map(Object::toString)
    .collect(Collectors.joining(", "));

System.out.println(line); // 1, 2, 3

Comparaison avec l’approche « manuelle »

Avant le Stream API:

import java.util.ArrayList;
import java.util.List;

List<String> merged = new ArrayList<>(listA);
merged.addAll(listB);

Avec le Stream API:

import java.util.stream.Collectors;

List<String> merged = Stream.concat(listA.stream(), listB.stream())
    .collect(Collectors.toList());

Avantages des streams: vous pouvez insérer des opérations intermédiaires (filter, map, etc.) avant et après la fusion, et travailler avec n’importe quelle source de données.

7. Erreurs courantes lors de la fusion de flux

Erreur n° 1: réutilisation d’un flux. Un flux ne peut être utilisé qu’une seule fois. Il est interdit de fusionner un flux avec lui-même.

import java.util.stream.Stream;

Stream<String> s = Stream.of("a", "b");
Stream<String> merged = Stream.concat(s, s); // Erreur ! s sera réutilisé

Erreur n° 2: tentative de fusionner un flux infini avec un flux fini. Si le premier flux est infini, le second ne commencera jamais.

import java.util.stream.Stream;

Stream<Integer> infinite = Stream.generate(() -> 1);
Stream<Integer> finite = Stream.of(2, 3);

Stream<Integer> merged = Stream.concat(infinite, finite);
// merged.limit(10).forEach(System.out::println); // finite ne figurera pas dans le résultat

Erreur n° 3: joining pour des non-chaînes. Le collecteur Collectors.joining attend un flux de chaînes. Pour des nombres, convertissez d’abord en chaînes:

import java.util.List;
import java.util.stream.Collectors;

List<Integer> numbers = List.of(1, 2, 3);
// numbers.stream().collect(Collectors.joining(", ")); // Erreur de compilation !

String joined = numbers.stream()
    .map(Object::toString)
    .collect(Collectors.joining(", "));

Erreur n° 4: perturbation de l’ordre. Si l’ordre est important, utilisez concat ou un flatMap ordonné. Des méthodes comme unordered() peuvent rompre l’ordre déterministe de sortie.

1
Mission
JAVA 25 SELF, niveau 32, leçon 1
Bloqué
Récolte pour une salade de fruits grandiose 🍎🍊
Récolte pour une salade de fruits grandiose 🍎🍊
1
Mission
JAVA 25 SELF, niveau 32, leçon 1
Bloqué
Présentation de la vitrine d'objets célestes 🚀
Présentation de la vitrine d'objets célestes 🚀
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION