API Java Stream: une alternative pour effectuer des opérations sur vos données
Qu’est-ce qu’un flux Java?
L’API Stream a été introduite à partir de Java 8 et a apporté un moyen très simple d’effectuer certains types d’opérations sur vos données. Selon la documentation de Java, un flux est défini comme
Une séquence d’éléments prenant en charge les opérations d’agrégation séquentielles et parallèles
Certaines des opérations que vous pourrez effectuer avec les flux que nous couvrirons ici sont les suivantes:
1- Filtre
2 – Carte
3 – Compter
4 – Vérifiez si tous les éléments d’un flux correspondent à un critère
5 – Vérifiez si au moins un élément d’un flux correspond à un critère
Comment créer un flux?
Tout d’abord, nous devons examiner comment créer un flux et comment convertir des listes en flux et inversement.
Création d’un nouveau flux
La façon la plus simple de créer un flux est d’appeler le statique of
méthode de la Stream
classe. Cette méthode accepte un nombre variable d’arguments où vous pouvez transmettre vos valeurs.
Stream.of(1, 2, 3, 4);
Conversion de listes en flux
Comme mentionné précédemment, vous devriez également pouvoir convertir un ArrayList
ou List
à un Stream
. Vous pouvez le faire facilement en appelant le stream()
méthode sur votre liste.
List anArrayList = new ArrayList<>();
// Elements might get added to your List here...
anArrayList.stream(); // You now have a Stream!
Conversion de flux en listes
Une fois que vous avez effectué les actions que vous vouliez sur votre Stream
, vous pouvez le reconvertir en List
. Pour ce faire, vous pouvez appeler le collect
méthode et passer en argument Collectors.toList()
.
List filteredList = myList.stream()
.filter(p -> p > 0)
.collect(Collectors.toList());
Opérations de flux
Filtre
Vous devez utiliser cette opération lorsque vous souhaitez renvoyer un nouveau flux avec tous les objets du flux précédent qui correspondent à une condition spécifique.
L’argument de cette méthode sera un lambda où l’argument sera une condition booléenne. Dans cet exemple, nous disons que toute valeur où val % 2 == 0
est true
, nous l’ajouterons au nouveau Stream
nous revenons, d’autres seront jetés.
List myList = Stream
.of(1, 2, 3, 4)
.filter(val -> val % 2 == 0)
.collect(Collectors.toList());
Carte
Vous devriez pouvoir utiliser cette opération lorsque vous souhaitez mapper chaque objet d’un flux et renvoyer un nouveau flux avec ces objets nouvellement mappés. Cette méthode accepte comme argument un lambda où vous retournez le nouvel objet que vous souhaitez retourner.
Dans cet exemple, nous commençons par un flux de valeurs 1 à 4 et nous mappons ces valeurs et en ajoutons 2 à chacune d’elles. Notre nouveau flux contiendra désormais les valeurs 2, 4, 5 et 6 et sera finalement converti en liste.
List myList = Stream
.of(1, 2, 3, 4)
.map(val -> val + 2)
.collect(Collectors.toList());
Compter
Vous devriez pouvoir utiliser cette opération lorsque vous souhaitez compter le nombre d’objets dans un flux. Cette méthode renvoie une valeur de type long.
Dans cet exemple, nous commençons avec un flux de valeurs 1 à 4 et nous devrions pouvoir obtenir une valeur de 4 de type long.
long numberOfElements = Stream
.of(1, 2, 3, 4)
.count();
N’importe quel match
Vous devez utiliser cette opération lorsque vous souhaitez vérifier s’il existe au moins un élément dans le flux qui correspond aux critères. Cette méthode retournera une valeur booléenne et non un flux comme une carte ou un filtre.
Cette méthode prendra comme argument un lambda qui retourne une condition booléenne où au moins un élément de votre flux doit correspondre. Dans ce scénario, où nous avons un flux avec des valeurs de 1 à 4, nous avons une correspondance où il y a une valeur de 2, donc cela retournera vrai.
List myList = Stream
.of(1, 2, 3, 4)
.anyMatch(val -> val == 2)
.collect(Collectors.toList());
Tous les matchs
Vous devez utiliser cette opération lorsque vous souhaitez vérifier si tous les éléments du flux correspondent à des critères spécifiques. Cette méthode retournera une valeur booléenne et non un flux comme une carte ou un filtre.
Cette méthode prendra comme argument un lambda qui retourne une condition booléenne dans laquelle tous les éléments de votre flux doivent correspondre. Dans ce scénario, où nous avons un flux avec des valeurs de 1 à 4, nous avons toutes les valeurs correspondant à la condition où elles sont supérieures à 0, donc cela retournera vrai.
List myList = Stream
.of(1, 2, 3, 4)
.anyMatch(val -> val > 0)
.collect(Collectors.toList());
Inclure les corps dans le flux lambdas
Tous les exemples que nous avons passés en revue sont relativement simples et ne nécessitent aucune opération complexe. Passons en revue un scénario plus compliqué où un corps doit être implémenté pour l’une de nos méthodes de flux.
Supposons que vous ayez une classe parent appelée Vehicle
et deux classes qui en héritent appelées Car
et Boat
. Nous ignorerons les détails de la classe à des fins de simplicité.
public abstract class Vehicle { }
public class Car extends Vehicle { }
public class Boat extends Vehicle { }
Supposons maintenant que vous ayez initialement une liste avec deux chaînes « Car » et « Boat » et que vous souhaitiez créer une nouvelle instance de l’une des classes ci-dessus en fonction de la chaîne elle-même.
Sur tous les exemples dont nous avons discuté auparavant, nous n’avons pas eu besoin de faire aucun type de vérification conditionnelle sur notre map
une fonction. Dans ce cas, nous pouvons ajouter les accolades et renvoyer les objets correspondants en fonction des chaînes que nous avons sur le flux.
List vehicles = Stream
.of("Car", "Boat")
.map(vehicle -> {
if (vehicle.equalsIgnoreCase("boat")) {
return new Boat();
}
return new Car();
})
.collect(Collectors.toList());
Chaînage de flux
Certaines opérations de flux renvoyant un nouveau flux, vous pouvez les chaîner. Prenons comme exemple un flux contenant des objets de type Car
qui a deux propriétés appelées numberOfWheels
de type int
et une propriété appelée make
de type String.
Dans cet exemple, nous allons récupérer toutes les voitures qui ont plus de quatre roues et créer une chaîne pour chacune de ces voitures en disant «Une voiture MAKE a NUMBER_OF_WHEELS de roues».
List vehicles = cars
.filter(car -> car.getNumberOfWheels() > 4)
.map(car -> "A " + car.getMake() + " car has " + car.getNumberOfWheels() + " of wheels")
.collect(Collectors.toList());
Pourquoi devrais-je utiliser l’API Stream?
En quelques mots: un code plus simple et plus propre!
Imaginez avoir cet extrait de code que vous avez écrit par exemple dans un monde où il n’y a pas d’API Stream.
boolean isSUV = vehicles
.stream()
.map(vehicle -> new Vehicle(vehicle))
.filter(vehicle -> vehicle.getNumberOfWheels() > 2)
.allMatch(vehicle -> vehicle.isSportsUtilityVehicle());
Bien qu’il existe de nombreuses façons d’écrire le code ci-dessus sans utiliser l’API Stream, nous pouvons jeter un œil à cet extrait de code qui tente d’obtenir les mêmes fonctionnalités que celle ci-dessus sans tirer parti de l’API Stream. Ce code est plus compliqué à suivre et plus sujet aux erreurs.
boolean allMatch = false;for (String vehicleStr: vehicles) {
Vehicle vehicle = new Vehicle(vehicleStr);
if (vehicle.getNumberOfWheels() > 2) {
if (vehicle.isSportsUtilityVehicle()) {
allMatch = true;
} else {
break;
}
}
}boolean isSUV = allMatch;
Je tiens à souligner que ce code n’est pas une correspondance exacte de ce que fait le code avec l’API Stream, il atteint simplement le même résultat final de la manière la plus simple à laquelle je puisse penser.
J’espère que vous avez maintenant une meilleure compréhension du fonctionnement de l’API Java Stream. Prendre plaisir.