Guide des flux Java : Un guide pour débutants

Bonjour à tous, futurs magiciens de Java ! Aujourd'hui, nous allons entreprendre un voyage passionnant à travers le monde des flux Java. Ne vous inquiétez pas si vous êtes nouveau dans la programmation - je serai votre guide amical, et nous avancerons pas à pas. À la fin de ce tutoriel, vous serez capable de manipuler les données comme un pro !

Java - Streams

Qu'est-ce qu'un flux en Java ?

Imaginez que vous êtes dans un restaurant de sushi à convoyeur. Les assiettes de sushi défilent devant vous sans arrêt, et vous pouvez choisir ce que vous voulez. C'est essentiellement ce qu'est un flux en Java - c'est une séquence d'éléments que vous pouvez traiter un par un, sans avoir besoin de les stocker tous en mémoire à la fois.

Les flux ont été introduits en Java 8 pour faciliter et rendre plus efficace le travail avec des collections de données. Ils nous permettent d'effectuer des opérations sur les données d'une manière plus déclarative, en disant à Java ce que nous voulons faire plutôt que comment le faire.

Créer des flux en Java

Commençons par créer notre premier flux. Il y a plusieurs façons de le faire, mais nous allons commencer avec un exemple simple :

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

public class ExempleFlux {
public static void main(String[] args) {
// Créer un flux à partir d'une liste
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
Stream<String> fluxFruits = fruits.stream();

// Créer un flux directement
Stream<Integer> fluxNumeros = Stream.of(1, 2, 3, 4, 5);
}
}

Dans cet exemple, nous avons créé deux flux. Le premier, fluxFruits, est créé à partir d'une liste de fruits. Le second, fluxNumeros, est créé directement en utilisant la méthode Stream.of().

Opérations courantes sur les flux

Maintenant que nous avons nos flux, examinons quelques opérations courantes que nous pouvons effectuer sur eux.

Méthode forEach

La méthode forEach est comme un robot amical qui parcourt chaque élément de votre flux et fait quelque chose avec. Utilisons-la pour imprimer nos fruits :

fruits.stream().forEach(fruit -> System.out.println(fruit));

Cela imprime :

apple
banana
cherry
date

Ici, fruit -> System.out.println(fruit) est appelée une expression lambda. C'est un moyen abrégé de dire à Java ce que faire avec chaque élément.

Méthode map

La méthode map est comme une baguette magique qui transforme chaque élément du flux. Utilisons-la pour mettre tous nos fruits en majuscules :

List<String> fruitsMajuscules = fruits.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(fruitsMajuscules);

Cela imprime :

[APPLE, BANANA, CHERRY, DATE]

La méthode String::toUpperCase est une référence de méthode, une autre fonctionnalité pratique de Java qui dit "utilisez la méthode toUpperCase sur chaque String".

Méthode filter

La méthode filter est comme un vigile de boîte de nuit, laissant passer seulement certains éléments. Utilisons-la pour ne garder que les fruits qui commencent par 'a' :

List<String> fruitsA = fruits.stream()
.filter(fruit -> fruit.startsWith("a"))
.collect(Collectors.toList());
System.out.println(fruitsA);

Cela imprime :

[apple]

Méthode limit

La méthode limit est comme dire "Je ne veux que ceux-ci, merci !" Elle restreint le flux à un certain nombre d'éléments :

List<String> deuxPremiersFruits = fruits.stream()
.limit(2)
.collect(Collectors.toList());
System.out.println(deuxPremiersFruits);

Cela imprime :

[apple, banana]

Méthode sorted

La méthode sorted est comme ranger votre étagère de livres. Elle met les éléments en ordre :

List<String> fruitsTries = fruits.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(fruitsTries);

Cela imprime :

[apple, banana, cherry, date]

Traitement en parallèle

Une des choses sympas avec les flux, c'est qu'ils peuvent être traités en parallèle, ce qui peut accélérer les opérations sur de grandes ensembles de données. Vous pouvez créer un flux parallèle comme ceci :

fruits.parallelStream().forEach(fruit -> System.out.println(fruit + " " + Thread.currentThread().getName()));

Cela pourrait imprimer quelque chose comme :

banana ForkJoinPool.commonPool-worker-1
apple main
date ForkJoinPool.commonPool-worker-2
cherry ForkJoinPool.commonPool-worker-3

L'ordre pourrait être différent à chaque exécution, car les fruits sont traités en parallèle !

Collecteurs

Les collecteurs sont des outils spéciaux qui nous aident à rassembler les résultats des opérations de flux. Nous avons utilisé collect(Collectors.toList()) dans nos exemples pour transformer les résultats de flux en listes. Il existe de nombreux autres collecteurs utiles :

// Joindre les éléments en une chaîne de caractères
String chaineFruits = fruits.stream().collect(Collectors.joining(", "));
System.out.println(chaineFruits);  // Imprime : apple, banana, cherry, date

// Compter les éléments
long nombreFruits = fruits.stream().collect(Collectors.counting());
System.out.println(nombreFruits);  // Imprime : 4

// Groupes d'éléments
Map<Character, List<String>> groupesFruits = fruits.stream()
.collect(Collectors.groupingBy(fruit -> fruit.charAt(0)));
System.out.println(groupesFruits);  // Imprime : {a=[apple], b=[banana], c=[cherry], d=[date]}

Statistiques

Les flux peuvent également nous aider à calculer des statistiques sur des données numériques :

List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numeros.stream().mapToInt(Integer::intValue).summaryStatistics();
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());

Cela imprime :

Count: 5
Sum: 15
Min: 1
Max: 5
Average: 3.0

Exemple de flux Java

Mettons tout cela ensemble avec un exemple plus complexe. Imaginons que nous avons une liste d'étudiants avec leur âge et leurs notes :

class Etudiant {
String nom;
int age;
double note;

Etudiant(String nom, int age, double note) {
this.nom = nom;
this.age = age;
this.note = note;
}
}

public class ExempleFlux {
public static void main(String[] args) {
List<Etudiant> etudiants = Arrays.asList(
new Etudiant("Alice", 22, 90.5),
new Etudiant("Bob", 20, 85.0),
new Etudiant("Charlie", 21, 92.3),
new Etudiant("David", 23, 88.7)
);

// Obtenir la note moyenne des étudiants de plus de 21 ans
double noteMoyenne = etudiants.stream()
.filter(etudiant -> etudiant.age > 21)
.mapToDouble(etudiant -> etudiant.note)
.average()
.orElse(0.0);

System.out.println("Note moyenne des étudiants de plus de 21 ans : " + noteMoyenne);

// Obtenir les noms des deux meilleurs étudiants par note
List<String> deuxMeilleursEtudiants = etudiants.stream()
.sorted((e1, e2) -> Double.compare(e2.note, e1.note))
.limit(2)
.map(etudiant -> etudiant.nom)
.collect(Collectors.toList());

System.out.println("Deux meilleurs étudiants : " + deuxMeilleursEtudiants);
}
}

Cet exemple montre comment nous pouvons chaîner plusieurs opérations de flux pour effectuer des tâches de traitement de données complexes d'une manière concise et lisible.

Et voilà, les amis ! Vous avez刚刚迈出了进入Java流世界的第一步。记住,熟能生巧,所以不要害怕尝试这些概念。在你意识到之前,你将能够像专业人士一样处理数据!快乐编码!

Credits: Image by storyset