JAVA 8 – API Stream – Introduction sur des collections

Java 8 arrive avec une toute nouvelle API 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 v
Pierre LISERONMis à jour le 9 Mai 2014
Java 8 api stream collections

Introduction à lutilisation des STREAM

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, daméliorer la performance ainsi daugmenter la lisibilité dun certain nombre de code. Nous allons essayer ici de voir comment les utiliser et dans de voir dans quels cas lutilisation 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 nimporte quel objet de type Collections, par exemple une liste (List). Ici nous allons partir dun exemple ou nous avons une liste de Commande. Une commande ayant un numéro de commande ainsi quun montant. Cest un exemple classique que nous rencontrons tous les jours dans le développements dapplications 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. Dabord, 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 lopération, chaque commande aura comme numéro la date (en string) de quand la commande a été enregistré.

Lutilisation de FILTER

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 lutilisation 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 lutilisation des lambda simplifie très clairement la lecture et nul besoin dutiliser une boucle sur les commandes afin de réaliser notre filtre. Lexpression du filtre x.numero.startsWith() est très lisible même pour quelquun qui ne connait pas JAVA 8.

Nous allons voir maintenant toutes les autres instructions de base qui sont possible sur des stream().

Lutilisation de MAP sur les STREAM

Lutilisation de linstruction 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 lutilisation de map, nous avons choisi de récupérer que le montant à retourner dans la listeCommandeMai qui devient une liste de Double.

Mais lutilisation 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.

Lopération COLLECT sur les STREAM

En fonction de ce que vous souhaiter récupérer, il est directement possible dappeler sur votre STREAM la méthode COLLECT (opération terminale). Ici nous lutilisons 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.

Lopération GET sur les STREAM

Lopération GET permet de ne récupérer quun seul élément et est utilisée pour toutes les opérations sur les STREAM qui sont amenées à ne récupérer quun élément. Par exemple, MAX, MIN

Lutilisation de linstruction SORTED et DISTINCT sur les STREAM

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 lorder naturel est utilisé car aucun comparator nest 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 linstruction distinct(). Ne même lordre 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());

Lutilisation de linstruction MAX ou MIN sur les STREAM

Très simplement, pour récupérer le max des commandes, lutilisation 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 lon utilise le méthode GET pour récupérer le résultat car max par définition ne renvoie quun é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.

Lutilisation de la commande LIMIT sur les STREAM

Il est aussi possible de limiter le nombre de résultat en sortie dun stream avec lutilisation 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());

.

Lutilisation de la methode COUNT sur les STREAM

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 lAPI STREAM JAVA 8. Donc un article suivant nous verrons quels sont les impacts sur les performances de lutilation des streams. En particulier le rôle des collections et la comparaison entre lutilisation de stream et parallelStream.