JavaLangage de développement très populaire ! 8 arrive avec une toute nouvelle APIUne API est un programme permettant à deux applications distinctes de communiquer entre elles et d’échanger des données. Stream qui utilise les Lambda. Cette nouvelle API offre la possibilité de simplifier l’écriture, d’améliorer la performance ainsi d’augmenter la lisibilité d’un certain nombre de code. Nous allons essayer ici de voir comment les utiliser et dans de voir dans quels cas l’utilisation de cette API est utile. Dans un second article, nous nous interresserons aux performances des stream et mode « normal » et en mode « parallel ».
Ici, nous nous concentrerons que sur des streams créés sur des collections et en particulier depuis une liste.
Un stream peut se créer sur n’importe quel objet de type Collections, par exemple une liste (List). Ici nous allons partir d’un exemple ou nous avons une liste de Commande. Une commande ayant un numéro de commande ainsi qu’un montant. C’est un exemple classique que nous rencontrons tous les jours dans le développements d’applications WEB. Voici notre gentille classe Commande.
public class Commande {
String numero;
double montant;
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public double getMontant() {
return montant;
}
public void setMontant(double montant) {
this.montant = montant;
}
public Commande(String numero, double montant) {
super();
this.numero = numero;
this.montant = montant;
}
}
Nous allons commencer par un exemple très simple qui va nous permettre de trier une liste de commande en ne selectionnant que les commandes du mois de mai 2014. D’abord, créons une liste de commande avec des dates et des montants différents.
List listeCommandes = new ArrayList();
listeCommandes.add(new Commande("20140509", 113.12));
listeCommandes.add(new Commande("20140508", 113.07));
listeCommandes.add(new Commande("20140507", 356.03));
listeCommandes.add(new Commande("20140512", 78.94));
listeCommandes.add(new Commande("20140409", 163.23));
listeCommandes.add(new Commande("20140429", 982.34));
listeCommandes.add(new Commande("20140508", 172.89));
Pour simplifier l’opération, chaque commande aura comme numéro la date (en string) de quand la commande a été enregistré.
Avant JAVA 8, pour faire ceci, il faut écrire quelque chose comme:
List lListeCommandeMoisMai =new ArrayList();
for (Commande commande : listeCommandes) {
if(commande.numero.startsWith("201405")){
lListeCommandeMoisMai.add(commande);
}
}
Avec JAVA 8 et l’utilisation des stream et lambda, il suffit d’écrire:
List lListeCommandeMoisMai = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405"))
.collect(Collectors.toList());
Pour afficher le résultat:
for (Commande commande : lListeCommandeMoisMai) {
System.out.println(commande.numero);
}
Voici, donc notre premier stream, qui a permis de trier notre liste de commande en ne prenant en compte que les commandes de mai « 201405 ». On se rend compte aisément que l’écriture de ce même traitement avec des streams et l’utilisation des lambda simplifie très clairement la lecture et nul besoin d’utiliser une boucle sur les commandes afin de réaliser notre filtre. L’expression du filtre x.numero.startsWith() est très lisible même pour quelqu’un qui ne connait pas JAVA 8.
Nous allons voir maintenant toutes les autres instructions de base qui sont possible sur des stream().
L’utilisation de l’instruction map() permet de choisir quel élement on veut « récupérer » dans notre steam. Par exemple, supposons que nous souhaitons récupérer les montants des commandes du mois de mai.
List listeCommandeMai = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405")).map(x -> x.montant)
.collect(Collectors.toList());
Avec l’utilisation de map, nous avons choisi de récupérer que le montant à retourner dans la listeCommandeMai qui devient une liste de Double.
Mais l’utilisation de map permet aussi de modifier directement ce que nous allons récupérer! Par exemple, imaginons que nous voulions calculer la TVA sur ce montant. Nous pouvons écrire:
List listeTVA = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405")).map(x -> x.montant*0.2)
.collect(Collectors.toList());
Ainsi, en une opération, nous avons et filter et récupérer la TVA sur toutes les commandes, le tout en très peu de ligne de codes!
JAVADOC: Returns a stream consisting of the results of applying the given function to the elements of this stream.
En fonction de ce que vous souhaiter récupérer, il est directement possible d’appeler sur votre STREAM la méthode COLLECT (opération terminale). Ici nous l’utilisons pour récupérer nos éléménts dans une liste.
JAVADOC: Performs a mutable reduction operation on the elements of this stream using a Collector.
L’opération GET permet de ne récupérer qu’un seul élément et est utilisée pour toutes les opérations sur les STREAM qui sont amenées à ne récupérer qu’un élément. Par exemple, MAX, MIN…
Les streams offrent aussi la possibilité de trier directement le résultat de sorti, par exemple si nous voulions récupérer les TVA par ordre croissant, il suffit d’écrire: Ici l’order naturel est utilisé car aucun comparator n’est passé en paramètre de la méthode sorted.
List lListeCommandeMai = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405")).map(x -> x.montant*0.2)
.sorted()
.collect(Collectors.toList());
Mais il est aussi possible de ne choisir que des éléments distinct avec l’instruction distinct(). Ne même l’ordre naturel est utilisé pour réaliser le disctinct.
List lListeCommandeMai = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405")).map(x -> x.montant*0.2)
.distinct()
.collect(Collectors.toList());
List lListeCommandeMai= listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405"))
.sorted((x1, x2) -> (int)(x1.montant - x2.montant))
.collect(Collectors.toList());
Très simplement, pour récupérer le max des commandes, l’utilisation des STREAM simplifie encore l’écriture. Ici nous récupérer la commande avec le plus gros montant en spécifiant nous même notre comparator. Attention, ici, on remarque que l’on utilise le méthode GET pour récupérer le résultat car max par définition ne renvoie qu’un élément.
Commande commande = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405"))
.max((x1, x2) -> (int) (x1.montant - x2.montant)).get();
De la même manière nous pouvons utiliser la méthode min pour récupérer le plus petit élément.
Il est aussi possible de limiter le nombre de résultat en sortie d’un stream avec l’utilisation de la commande limit(). Ici nous allons récupérer les deux commandes avec le plus gros montant.
List lListeCommandeMai = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405"))
.sorted((x1, x2) -> (int)(x1.montant - x2.montant))
.limit(2)
.collect(Collectors.toList());
.
Il est aussi parfois interessant de juster compter le nombre d’éléments présent dans notre filtre. Pour ce faire, on peut utiliser la méthode COUNT directement sur le stream
long nombreElement = listeCommandes.stream()
.filter(x -> x.numero.startsWith("201405"))
.count();
JAVADOC COUNT: Returns the count of elements in this stream.
Source: http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Voilà une courte introduction sur l’API STREAM JAVA 8. Donc un article suivant nous verrons quels sont les impacts sur les performances de l’utilation des streams. En particulier le rôle des collections et la comparaison entre l’utilisation de stream et parallelStream.
Etude de la performance de l'utilisation de la nouvelle API JAVA 8 Stream pour le traitement de string (chaîne de caractère)
Découvrez la planche #4 !
Le DevOps, c’est quoi et comment se lancer ? On vous donne notre définition du DevOps, nos outils préférés et nos conseils pour réussir un projet en DevOps