Exemples Concrets d'Utilisation de l'API Stream
Le code ne concerne pas seulement la fonctionnalité—il s'agit aussi de la lisibilité. Un code bien structuré est plus facile à maintenir, modifier et étendre.
Vous êtes un développeur chargé de relire le code de quelqu'un d'autre et de l'améliorer. Dans les projets réels, le code est souvent écrit dans la précipitation, copié depuis différentes parties du programme, ou simplement conçu sans souci de lisibilité. Votre tâche n'est pas seulement de comprendre le code, mais aussi de l'améliorer—le rendre plus propre, plus concise et plus facile à maintenir.
Actuellement, vous effectuez une revue de code. Vous allez analyser un extrait réel de code, identifier ses points faibles et le refactoriser étape par étape en utilisant le Stream API.
Pour commencer
Imaginez que vous avez une boutique en ligne et que vous devez identifier les utilisateurs actifs ayant passé au moins trois commandes de 10 000 $ ou plus. Cela aidera l'équipe marketing à reconnaître les clients les plus précieux et à leur proposer des offres personnalisées.
Voici le code initial avant refactorisation :
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsers.add(user); } } } System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Vous disposez de deux classes principales :
Order représente une commande et contient un champ total stockant le montant de la commande.
User représente un client du magasin avec trois champs :
name(nom de l'utilisateur) ;active(indicateur de statut) ;orders(liste des commandes).
Chaque User contient une liste d'objets Order, modélisant un scénario réel où un client passe plusieurs commandes.
Dans main, une liste d'utilisateurs est créée, chacun avec son propre ensemble de commandes. Le programme parcourt ensuite cette liste et vérifie si un utilisateur est actif. Sinon, il est ignoré.
Ensuite, le programme parcourt les commandes de l'utilisateur et compte combien d'entre elles sont d'au moins 10 000 $. Si l'utilisateur possède au moins trois commandes éligibles, il est ajouté à la liste premiumUsers.
Une fois tous les utilisateurs traités, le programme affiche les utilisateurs premium.
Problèmes du code
- Trop de boucles imbriquées – rend la lecture et la compréhension plus difficiles ;
- Code redondant – trop de vérifications manuelles et de variables intermédiaires ;
- Manque de style déclaratif – le code ressemble à un traitement de données bas niveau plutôt qu'à une logique de haut niveau.
Vous allez maintenant remanier ce code étape par étape en utilisant le Stream API afin d'améliorer la lisibilité, de réduire la redondance et de le rendre plus efficace.
Refactorisation du code
La première étape consiste à supprimer le if (user.isActive()) externe et à l’intégrer directement dans l’API Stream :
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Désormais, le code est plus déclaratif et montre clairement que vous filtrez les utilisateurs actifs. La condition if superflue a disparu—la logique est maintenant directement intégrée à l’API Stream. Cependant, il ne s’agit que de la préparation des données, poursuivons !
Ensuite, remplaçons la boucle for imbriquée (for (Order order : user.getOrders())) par un stream() à l’intérieur du filter :
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3) // Count orders directly in Stream API
.toList();
En éliminant le comptage manuel, le code devient plus clair et plus lisible—désormais, count() gère cela pour nous, permettant de travailler avec le stream sans variables supplémentaires.
Code final refactorisé
Vous disposez maintenant d'une solution entièrement refactorisée qui résout la tâche de manière déclarative et concise :
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Le code est désormais plus court et plus clair car, au lieu d'une série de vérifications manuelles, une approche déclarative est utilisée—en se concentrant sur ce qui doit être fait plutôt que de détailler chaque étape du processus. Cela élimine le besoin de boucles imbriquées, rendant le code plus facile à lire et à maintenir.
En exploitant le Stream API, il est possible de combiner de manière transparente le filtrage, le comptage et la collecte de données dans un seul flux, rendant le code plus expressif et efficace.
Merci pour vos commentaires !
Demandez à l'IA
Demandez à l'IA
Posez n'importe quelle question ou essayez l'une des questions suggérées pour commencer notre discussion
Awesome!
Completion rate improved to 2.33
Exemples Concrets d'Utilisation de l'API Stream
Glissez pour afficher le menu
Le code ne concerne pas seulement la fonctionnalité—il s'agit aussi de la lisibilité. Un code bien structuré est plus facile à maintenir, modifier et étendre.
Vous êtes un développeur chargé de relire le code de quelqu'un d'autre et de l'améliorer. Dans les projets réels, le code est souvent écrit dans la précipitation, copié depuis différentes parties du programme, ou simplement conçu sans souci de lisibilité. Votre tâche n'est pas seulement de comprendre le code, mais aussi de l'améliorer—le rendre plus propre, plus concise et plus facile à maintenir.
Actuellement, vous effectuez une revue de code. Vous allez analyser un extrait réel de code, identifier ses points faibles et le refactoriser étape par étape en utilisant le Stream API.
Pour commencer
Imaginez que vous avez une boutique en ligne et que vous devez identifier les utilisateurs actifs ayant passé au moins trois commandes de 10 000 $ ou plus. Cela aidera l'équipe marketing à reconnaître les clients les plus précieux et à leur proposer des offres personnalisées.
Voici le code initial avant refactorisation :
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsers.add(user); } } } System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Vous disposez de deux classes principales :
Order représente une commande et contient un champ total stockant le montant de la commande.
User représente un client du magasin avec trois champs :
name(nom de l'utilisateur) ;active(indicateur de statut) ;orders(liste des commandes).
Chaque User contient une liste d'objets Order, modélisant un scénario réel où un client passe plusieurs commandes.
Dans main, une liste d'utilisateurs est créée, chacun avec son propre ensemble de commandes. Le programme parcourt ensuite cette liste et vérifie si un utilisateur est actif. Sinon, il est ignoré.
Ensuite, le programme parcourt les commandes de l'utilisateur et compte combien d'entre elles sont d'au moins 10 000 $. Si l'utilisateur possède au moins trois commandes éligibles, il est ajouté à la liste premiumUsers.
Une fois tous les utilisateurs traités, le programme affiche les utilisateurs premium.
Problèmes du code
- Trop de boucles imbriquées – rend la lecture et la compréhension plus difficiles ;
- Code redondant – trop de vérifications manuelles et de variables intermédiaires ;
- Manque de style déclaratif – le code ressemble à un traitement de données bas niveau plutôt qu'à une logique de haut niveau.
Vous allez maintenant remanier ce code étape par étape en utilisant le Stream API afin d'améliorer la lisibilité, de réduire la redondance et de le rendre plus efficace.
Refactorisation du code
La première étape consiste à supprimer le if (user.isActive()) externe et à l’intégrer directement dans l’API Stream :
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Désormais, le code est plus déclaratif et montre clairement que vous filtrez les utilisateurs actifs. La condition if superflue a disparu—la logique est maintenant directement intégrée à l’API Stream. Cependant, il ne s’agit que de la préparation des données, poursuivons !
Ensuite, remplaçons la boucle for imbriquée (for (Order order : user.getOrders())) par un stream() à l’intérieur du filter :
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3) // Count orders directly in Stream API
.toList();
En éliminant le comptage manuel, le code devient plus clair et plus lisible—désormais, count() gère cela pour nous, permettant de travailler avec le stream sans variables supplémentaires.
Code final refactorisé
Vous disposez maintenant d'une solution entièrement refactorisée qui résout la tâche de manière déclarative et concise :
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Le code est désormais plus court et plus clair car, au lieu d'une série de vérifications manuelles, une approche déclarative est utilisée—en se concentrant sur ce qui doit être fait plutôt que de détailler chaque étape du processus. Cela élimine le besoin de boucles imbriquées, rendant le code plus facile à lire et à maintenir.
En exploitant le Stream API, il est possible de combiner de manière transparente le filtrage, le comptage et la collecte de données dans un seul flux, rendant le code plus expressif et efficace.
Merci pour vos commentaires !