Les Streams sont des interfaces de la bibliothèque standard de Java qui permettent de traiter de manière efficace et parallèle de grandes quantités de données. Ils existent depuis la version 8 de Java et sont utiles pour le traitement de données en flux, comme la lecture d’un fichier ou la récupération de données à partir d’une base de données.
Les Streams permettent de traiter efficacement des séquences de données, collections et tableaux. Ces interfaces permettent de décrire de manière déclarative la transformation ou l’agrégation de données à effectuer, sans avoir à écrire explicitement les boucles et les tests de conditions nécessaires pour réaliser ces opérations habituellement écrites en mode impératif.
On peut :
Permet de réaliser des traitements de données de manière concise et lisible, avec une forte optimisation de performance grâce à l’utilisation de la parallélisation sous-jacente.
La parallélisation sous-jacente est une technique de programmation qui permet de décomposer un traitement de données en plusieurs tâches exécutées en parallèle sur différents processeurs ou coeurs de processeur. Cela permet d’accélérer le traitement de données en utilisant toute la puissance de calcul disponible sur un ordinateur.
Les Streams utilisent la parallélisation sous-jacente de manière transparente pour accélérer le traitement de données lorsque cela est possible. Par exemple, si vous utilisez la méthode parallelStream() au lieu de stream() sur une collection, le traitement de données sera exécuté en parallèle sur plusieurs coeurs de processeur s’il y en a disponibles, bien sûr !
Voici un exemple d’utilisation de la parallélisation sous-jacente avec les Streams en Java :
List<String> strings = Arrays.asList("avion", "bateau", "vélo", "moto", "fusée", "trotinette"); // Filtrage en parallèle avec Stream.parallelStream List<String> longStrings = strings.parallelStream() .filter(s -> s.length() > 4) .collect(Collectors.toList());
Résultat :
avion bateau trotinette
Ici, nous utilisons la méthode parallelStream pour créer un Stream parallèle sur une liste de chaînes de caractères, puis nous filtrons cette liste en ne conservant que les chaînes de longueur supérieure à 4. Le traitement de filtrage sera exécuté en parallèle sur plusieurs coeurs de processeur s’il y en a disponibles, ce qui peut accélérer le traitement de données si la liste est suffisamment grande.
Il est évident qu’il faudrait traiter une liste de plusieurs milliers, voire plusieurs millions de données pour que la parallélisation soit pertinente !
Remarque : la parallélisation sous-jacente n’est pas toujours bénéfique et peut même ralentir le traitement de données dans certains cas. Par exemple, si la liste est très petite comme c’est le cas ici, ou bien si le traitement de données est très simple, la surcharge de la parallélisation peut être supérieure au gain de performance. Bref, il est donc recommandé de mesurer les performances avec et sans parallélisation avant de décider si l’utilisation de parallelStream est appropriée ou pas pour votre cas d’utilisation.
Oui, comme avant, pour le plaisir de continuer avec les boucles et les conditions.
Voici ce que cela donnerait avec l’exemple plus haut :
List<String> strings = Arrays.asList("avion", "bateau", "vélo", "moto", "fusée", "trotinette"); for (String string : strings) { if (string.length() > 4) { System.out.println(string); } }
Personnellement, j’ai davantage l’impression de développer en écrivant ce bout de code ci-dessus, mais bon, il faut suivre l’évolution ;))
List<String> strings = Arrays.asList("avion", "bateau", "vélo", "moto", "fusée", "trotinette"); List<String> chaineTriee = strings.stream().sorted().collect(Collectors.toList());
List<String> strings = Arrays.asList("avion", "bateau", "vélo", "moto", "fusée", "trotinette"); long compter = strings.stream().filter(s -> s.length() > 5).count();
List<String> strings = Arrays.asList("avion", "bateau", "vélo", "moto", "fusée", "trotinette"); String chaineConcatenee = strings.stream().collect(Collectors.joining(", ")); // "avion, bateau, vélo, moto, fusée, trotinette"
List<String> strings = Arrays.asList("avion", "bateau", "vélo", "moto", "fusée", "trotinette"); Map<Integer, List<String>> stringsByLength = strings.stream().collect(Collectors.groupingBy(String::length));
List<String> strings = Arrays.asList("avion", "bateau", "vélo", "moto", "fusée", "trotinette", "planche à voile", "mobylette"); Optional<String> first = strings.stream().filter(s -> s.startsWith("m")).findFirst();
La classe Collectors est une classe utilitaire qui fournit une série de méthodes statiques pour la création de Collector.
Un Collector est un objet qui permet de regrouper des éléments d’un flux en un résultat unique, comme une liste, un ensemble ou une map.
Voici quelques exemples d’utilisation de la classe Collectors :
List<String> strings = Arrays.asList("123", "456", "789"); // Création d'une liste avec Collectors.toList() List<String> stringList = strings.stream().collect(Collectors.toList()); // Création d'un ensemble avec Collectors.toSet() Set<String> stringSet = strings.stream().collect(Collectors.toSet()); // Création d'une map avec Collectors.toMap() Map<String, Integer> stringMap = strings.stream() .collect(Collectors.toMap(s -> s, s -> s.length())); // Filtrage avec Stream.filter() List<String> longStrings = strings.stream() .filter(s -> s.length() > 2) .collect(Collectors.toList()); // Réduction des données avec Stream.reduce() Optional<String> reducedString = strings.stream() .reduce((s1, s2) -> s1 + "," + s2);
Dans ces exemples, nous utilisons les méthodes :
à partir d’un flux de chaînes de caractères strings.
La classe Collectors fournit également d’autres méthodes utiles pour la création de Collector, comme joining() pour concaténer les éléments d’un flux en une chaîne de caractères, ou averagingInt() pour calculer la moyenne des éléments d’un flux. (voir la documentation de Java).
Ici, il y a aussi les méthodes filter(), map() et reduce() pour filtrer une liste de chaînes de caractères, transformer chaque chaîne en majuscules et réduire toutes les chaînes en une seule chaîne concaténée.
Stream | ------------|------------ / \ DoubleStream IntStream | | ------------|------------- -----------|------------ / \ / \ LongStream FloatStream ShortStream
Comme vous pouvez le voir, la classe Stream est la classe de base de la hiérarchie des Streams. Elle définit les méthodes communes à tous les Streams, telles que map, filter, reduce, etc.
Les classes DoubleStream, IntStream, LongStream, FloatStream et ShortStream sont des sous-classes de Stream qui fournissent des implémentations spécialisées pour travailler avec des types primitifs (double, int, long, float et short). Elles définissent également des méthodes spécifiques à chaque type primitif, comme sum ou average.
Vous pouvez utiliser ces classes de la même manière que vous utiliseriez Stream, en fonction de vos besoins en matière de performance et de précision. Par exemple, si vous travaillez avec des nombres flottants de grande précision, vous pouvez utiliser DoubleStream ou FloatStream. Si vous travaillez avec des entiers, vous pouvez utiliser IntStream ou LongStream.
Il vous reste à lire la documentation Java en fonction de vos besoins. L’utilisation des Streams n’est pas une obligation et tout dépend de ce que vosu devez effectuer comme traitement et surtout de la quantité de données à traiter.
Bon courage !