Performance dans l'API Stream
Glissez pour afficher le menu
Vous avez réussi à refactoriser le code, le rendant plus propre, plus court et plus expressif.
Mais qu'en est-il des performances ? Quelle est l’efficacité de l’API Stream par rapport aux boucles traditionnelles ? Est-il possible d’accélérer le traitement avec parallelStream() ? Découvrons-le !
Mesure du temps d'exécution
Pour comparer objectivement les performances, nous allons créer un jeu de données de test avec 100 000 utilisateurs et différentes commandes. Ensuite, nous mettrons en œuvre trois méthodes de filtrage :
for-looptraditionnel – approche classique avec boucles imbriquées ;- API Stream (
stream()) – méthode déclarative moderne ; - API Parallel Stream (
parallelStream()) – traitement multithread.
Vous mesurerez le temps d'exécution à l'aide de System.nanoTime(), qui fournit des différences de temps de haute précision.
Implémentation du test
Vous générerez 100 000 utilisateurs, en veillant à ce qu'ils aient tous des commandes supérieures à 10 000 $, puis vous exécuterez les trois méthodes afin de déterminer laquelle offre les meilleures performances.
Main.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = generateUsers(100000); // Measuring traditional loop execution time long startTime = System.nanoTime(); List<User> premiumUsersLoop = 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) { premiumUsersLoop.add(user); } } } long endTime = System.nanoTime(); System.out.println("For-loop: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersStream = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Parallel Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersParallelStream = users.parallelStream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Parallel Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); } private static List<User> generateUsers(int count) { List<User> users = new ArrayList<>(); for (int i = 0; i < count; i++) { users.add(new User("User" + i, true, List.of( new Order(14000), new Order(5000), new Order(7000) ))); } return users; } } 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 + "'}"; } }
Après avoir exécuté le test avec 100 000 utilisateurs, les tendances suivantes peuvent être observées :
La boucle traditionnelle for-loop est plus rapide pour les opérations simples car elle évite la surcharge liée à la création de flux et d'objets supplémentaires. Elle offre les meilleures performances lorsque la vitesse brute est prioritaire.
Stream API est parfois légèrement plus lent en raison de la création d'objets supplémentaires, mais il améliore considérablement la lisibilité et la maintenabilité du code.
Parallel Stream API peut accélérer le traitement sur les systèmes multi-cœurs, mais pas systématiquement. Si le jeu de données est petit, la gestion des threads peut en réalité ralentir le processus. Il est optimal pour les calculs intensifs, mais pas lors de la modification de variables partagées, car les threads s'exécutent indépendamment.
Résumé
Stream API est un outil puissant qui rend le code plus lisible et concise. Cependant, en ce qui concerne les performances, il est important de comprendre ses limites. Dans certains cas, les boucles for traditionnelles sont plus rapides, en particulier lors du traitement de petits ensembles de données. parallelStream() peut améliorer la vitesse de traitement, mais il nécessite des tests pour s'assurer qu'il apporte réellement un avantage.
Ainsi, le choix de la bonne approche doit être intentionnel : si la lisibilité est prioritaire, utiliser Stream API ; si la performance est essentielle, il faut tester et mesurer !
1. Quelle approche est généralement la plus rapide pour des opérations simples ?
2. Pourquoi Stream API peut-il être plus lent qu'une boucle classique ?
3. Quand parallelStream() peut-il ralentir l'exécution ?
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