Streams
Les streams en Java sont introduits avec Java 8 et font partie du package java.util.stream. Ils permettent de traiter des collections (comme les listes ou les ensembles) de manière déclarative et souvent parallélisable, en s'inspirant du paradigme fonctionnel.
1. Définition rapide
Un Stream est une séquence d’éléments sur laquelle on peut effectuer une suite d’opérations, comme filtrer, transformer ou réduire, sans modifier la source de données.
2. Caractéristiques des Streams
- Non modifiant : le Stream ne modifie pas la collection source.
- Paresseux (lazy) : les opérations intermédiaires ne sont pas exécutées tant qu’une opération terminale n’est pas appelée.
- Peut être parallèle : exécution possible sur plusieurs threads avec
parallelStream().
3. Structure d’un pipeline de Stream
collection.stream() // Création du Stream
.filter(e -> e > 10) // Opération intermédiaire
.map(e -> e * 2) // Opération intermédiaire
.forEach(System.out::println); // Opération terminale
4. Types d’opérations
- Intermédiaires (retournent un Stream) :
filter(),map(),sorted(),distinct(),limit(),skip()
- Terminales (déclenchent l’exécution) :
forEach(),collect(),reduce(),count(),anyMatch(),findFirst()
5. Exemple
List<String> noms = List.of("Alice", "Bob", "Alex", "Charles");
noms.stream()
.filter(nom -> nom.startsWith("A"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
Résultat :
ALEX
ALICE
6. Cas typique avec collect()
List<Integer> result = List.of(1, 2, 3, 4, 5).stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
8. Avantages
- Code plus concis et lisible
- Meilleure expressivité
- Possibilité de paralléliser facilement
- Moins de mutations d’état
Stream primitifs
- Performance :
- Les flux ordinaires (
Stream<T>) fonctionnent avec des objets. Lorsque vous utilisez des types primitifs avec des flux ordinaires, ils sont automatiquement "emballés" (boxed) dans leurs classes wrapper correspondantes (Integer, Long, Double). - L'emballage et le déballage ont un coût en termes de performances. Les flux de types primitifs évitent cet emballage, ce qui peut améliorer considérablement les performances, en particulier pour les opérations numériques.
- Les flux ordinaires (
- Méthodes spécialisées :
- Les flux de types primitifs fournissent des méthodes spécialisées pour les opérations numériques, telles que
sum(),average(),range(), etrangeClosed(). Ces méthodes sont absentes des flux ordinaires.
- Les flux de types primitifs fournissent des méthodes spécialisées pour les opérations numériques, telles que
Création
IntStream.of(1, 2, 3) // à partir de valeurs
IntStream.range(1, 10) // de 1 à 9 (exclut 10)
IntStream.rangeClosed(1, 10) // de 1 à 10 (inclut 10)
Arrays.stream(new int[]{1, 2, 3}) // à partir d'un tableau
Opérations spécialisées
int somme = IntStream.rangeClosed(1, 100).sum(); // 5050
OptionalDouble moyenne = IntStream.of(10, 20, 30).average(); // 20.0
int max = IntStream.of(5, 2, 8, 1).max().getAsInt(); // 8
int[] tableau = IntStream.range(1, 6).toArray(); // [1, 2, 3, 4, 5]
Conversion vers un Stream d'objets (boxed())
Quand tu as besoin de collecter dans une List, il faut convertir avec boxed() :
List<Integer> liste = IntStream.range(1, 6)
.boxed()
.collect(Collectors.toList()); // [1, 2, 3, 4, 5]
Conversion depuis un Stream d'objets (mapToInt())
List<String> mots = List.of("Java", "est", "cool");
int totalCaracteres = mots.stream()
.mapToInt(String::length)
.sum(); // 11
Résultats optionnels : OptionalInt, OptionalDouble, OptionalLong
Les opérations average(), min(), max() retournent une variante primitive d'Optional car le stream pourrait être vide.
OptionalDouble moyenne = IntStream.of(10, 20, 30).average(); // OptionalDouble[20.0]
OptionalInt max = IntStream.of(5, 2, 8).max(); // OptionalInt[8]
Pour extraire la valeur, on utilise getAsDouble(), getAsInt() ou getAsLong() :
OptionalDouble moyenne = IntStream.of(10, 20, 30).average();
if (moyenne.isPresent()) {
System.out.println(moyenne.getAsDouble()); // 20.0
}
// Ou avec une valeur par défaut si le stream est vide
double resultat = IntStream.empty().average().orElse(0.0); // 0.0
getAsDouble()sur unOptionalDoublevide lanceNoSuchElementException. Toujours vérifier avecisPresent()avant, ou utiliserorElse()pour éviter l'exception.
Documentation Oracle : stream() (java.util.stream.Stream) · range, rangeClosed, sum, average, boxed, mapToInt (java.util.stream.IntStream)