IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Tutoriel pour découvrir l’API Collections de Google Guava

La classe Lists

Le but de cet article est l'exploration de la classe Lists de l'API Collections de la bibliothèque Guava de Google.

Comme exemple nous utiliserons un parc automobile, cela nous permettra de sonder de façon pratique et ludique les méthodes de cette classe.

En effet de par mon expérience, j'ai pu constater que certains développeurs (et parfois moi aussi) implémentaient des fonctionnalités alors que ces dernières étaient présentes dans des bibliothèques tierces.

Ainsi dans nos futurs projets respectons le vieil adage : "ne réinventez pas la roue".

Par ailleurs, je vous invite à créer un fil de discussion afin d’y laisser tous vos commentaires pour l’appréciation, voire pour l’amélioration des futurs tutoriels. Commentez Donner une note  l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Dans ce premier tutoriel sur l’exploration de la bibliothèque Guava de Google, nous allons parcourir les méthodes de la classe Lists afin de découvrir comment les exploiter de façon efficiente dans nos futurs projets. Ce tutoriel étant le premier d’une longue série à venir.

Par ailleurs comme il s'agit d'une découverte, je parcours la bibliothèque des méthodes de la classe Lists en y greffant des exemples simples. Force est de constater que la façon la plus triviale et adéquate de comprendre les fonctionnalités d’une classe utilitaire est d’implémenter chacune de ses méthodes à l’aide d’exemple. Ce tutoriel est avant tout pratique, mon fil conducteur est la page suivante https://github.com/google/guava/wiki/CollectionUtilitiesExplained#lists traitant des Collections de l'API Google Guava. Le lecteur devra donc s’attendre à voir la mise en œuvre des méthodes de la classe Lists de façon pragmatique et ludique. Souhaitons au lecteur d’apprécier toute la richesse de cette bibliothèque qui ne fait que s’améliorer au fur et à mesure des versions. Bonne lecture à tous.

II. Qu’est-ce que l’API Google Guava et pourquoi l’utiliser ?

L’API Google Guava est une bibliothèque contenant un ensemble de classes utilitaires fort utiles dans n’importe quel projet Java. En effet, cette API contient des classes allant du traitement des collections permettant de gérer des objets immuables, à la manipulation des chaînes de caractères, en passant par la gestion des entrées-sorties conversationnelles, et bien d’autres fonctionnalités qui facilitent la vie du développeur.

Dans cette série de tutoriels, nous n’aborderons que les classes utilitaires de l’API Collections. Cette dernière regroupe un ensemble de classes analogues à celles présentes dans le JDK, mais à la différence que ces dernières ont été améliorées par rapport à celles du JDK d’ORACLE.

En effet les développeurs de Google ont souhaité étendre les fonctionnalités des structures de données natives que nous utilisons au quotidien afin d’accroître notre productivité.

Le projet Guava représente le cœur du développement de Google, il représente des années de développement et une boîte à outils utilisée au quotidien par les développeurs de Google. Cette API est arrivée à maturité puisqu’elle est à l’heure actuelle à sa version 30.1 et ne cesse d’être agrémentée de nouvelles fonctionnalités.

Pour conclure cette section, nous dirons que dans ce premier volet nous découvrirons l’ensemble des méthodes utilitaires statiques composant la classe utilitaire Lists.

III. Ajouter Google Guava à votre projet

La bibliothèque Google Guava peut être ajoutée de plusieurs façons dans un projet. La façon la plus triviale est de récupérer le .jar depuis le site de Google, puis de placer ce dernier directement dans la bibliothèque de votre projet.

Une autre façon plus élégante est d’utiliser un outil industrialisé de build comme Apache Maven ou Gradle en y ajoutant la dépendance requise de Google Guava.

Installer Guava via Maven
Sélectionnez
1.
2.
3.
4.
5.
6.
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version>
</dependency>

IV. La classe utilitaire Lists

Comme je l’ai souligné, tout développeur familier avec le langage Java connaît l’utilité pratique des structures de données implémentant l’interface java.util.Collections (List, Map, Set, etc.).

Google Guava est allé encore plus loin dans l’implémentation des fonctionnalités sous-jacentes à l’utilisation de ces structures de données. Les développeurs de Google ont conçu de nombreuses méthodes utilitaires statiques permettant une gestion efficiente lorsque l’on travaille avec des objets de type List.

La classe com.google.common.collect.Lists contient des méthodes utilitaires statiques permettant de créer des instances de l'interface List du JDK.

Mieux qu’un long discours dans le chapitre suivant, nous mettrons en œuvre la richesse de cette bibliothèque à travers un cas pratique de gestion d’un parc automobile.

V. Implémentation des méthodes de la classe Lists

V-A. Les imports

Les imports
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import static java.lang.System.out;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;

V-A-1. Les attributs et blocs statiques

Créons une classe Application qui contiendra les attributs, les blocs et les méthodes statiques pour la gestion de notre parc automobile.

Les attributs et blocs statiques
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
public final class Application {
    private final static int NB_MARQUES_VEHICULES = 5;
    private final static String[] peugeot;
    private final static String[] toyota;
    private final static String[] opel;
    private final static String[] renault;
    private final static String[] citroen;
    private final static List<String> carburants; // types de carburants
    private final static List<Float> consommations;

    static { // les marques des véhicules
        peugeot = new String[NB_MARQUES_VEHICULES];
        peugeot[0] = "Peugeot Nouvelle SUV 3008";
        peugeot[1] = "Peugeot Nouvelle SUV 5008";
        peugeot[2] = "Peugeot Traveller/Expert Combi";
        peugeot[3] = "Peugeot Boxer";
        peugeot[4] = "Peugeot Tepee";

        toyota = new String[NB_MARQUES_VEHICULES];
        toyota[0] = "Toyota Auris";
        toyota[1] = "Toyota Nouvelle Yaris";
        toyota[2] = "Toyota Aygo X ";
        toyota[3] = "Toyota Prius+";
        toyota[4] = "Toyota Nouvelle C-HR";

        opel = new String[NB_MARQUES_VEHICULES];
        opel[0] = "Opel Calibra";
        opel[1] = "Opel Ascona";
        opel[2] = "Opel Corsa";
        opel[3] = "Opel Alambra";
        opel[4] = "Opel Meriva";

        renault = new String[NB_MARQUES_VEHICULES];
        renault[0] = "Renault Kangoo VP";
        renault[1] = "Renault Nouveau Grand Scenic";
        renault[2] = "Renault Alaskan";
        renault[3] = "Talisman Estate";
        renault[4] = "Renault Safrane";

        citroen = new String[NB_MARQUES_VEHICULES];
        citroen[0] = "Citroen Nouvelle C-Elysée";
        citroen[1] = "Citroen Nemo Multispace";
        citroen[2] = "Citroen C4 Aircross";
        citroen[3] = "Citroen Nouveau Grand C4 Picasso";
        citroen[4] = "Citroen Space Tourer Business Lounge";
    }
    
    static { // les types de carburants
        carburants = new ArrayList<>();
        carburants.add("Essence");
        carburants.add("Diesel");
        carburants.add("GNV"); // Gaz naturel pour véhicules
        carburants.add("Hybride");
        carburants.add("Biodiesel");
        carburants.add("Electric");
    }

    static { // la consommation du litre/100km, on voit qu'elle est très élevée.
        consommations = new ArrayList<>();
        consommations.add(4.3f);
        consommations.add(5.8f);
        consommations.add(6.9f);
        consommations.add(7.4f);
        consommations.add(8.5f);
        consommations.add(9.2f);
    }

    public static void main(String[] args) {
        // commençons par afficher la liste des véhicules de la marque Peugeot
        out.println(Arrays.asList(peugeot)); // [Peugeot Nouvelle SUV 3008, Peugeot Nouvelle SUV 5008, Peugeot Traveller/Expert Combi, Peugeot Boxer, Peugeot Tepee]
    }    
}

V-B. La méthode asList

V-B-1. asList(E first, E[] rest)

Ajoutons un nouveau véhicule au début de notre liste en utilisant la méthode asList(E first, E[] rest), puis affichons cette liste de véhicules. On constate que le véhicule a été ajouté au tout début de la liste.

Méthode asList(E first, E[] rest)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
final List<String> peugeotImmutableList = Lists.asList("Peugeot 107", peugeot);
peugeotImmutableList.forEach(System.out::println);
/*
Peugeot 107
Peugeot Nouvelle SUV 3008
Peugeot Nouvelle SUV 5008
Peugeot Traveller/Expert Combi
Peugeot Boxer
Peugeot Tepee
*/

À noter que la liste est immuable, donc si par la suite vous essayez d'ajouter un autre véhicule une exception sera levée.

UnsupportedOperationException
Sélectionnez
1.
peugeotImmutableList.add("Peugeot 107");

V-B-2. asList(E first, E second, E[] rest)

Dans ce cas de figure, il est préférable d’utiliser la méthode asList(E first, E second, E[] rest).

Méthode asList(E first, E second, E[] rest)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
final List<String> peugeotOtherImmutableList = Lists.asList("Peugeot 107", "Peugeot 108", peugeot);
peugeotOtherImmutableList.forEach(System.out::println);
/*
Peugeot 107
Peugeot 108
Peugeot Nouvelle SUV 3008
Peugeot Nouvelle SUV 5008
Peugeot Traveller/Expert Combi
Peugeot Boxer
Peugeot Tepee
*/

V-C. La méthode cartesianProduct

Nous souhaitons maintenant récupérer le produit cartésien, c’est-à-dire l’ensemble des types de carburants disponibles pour les véhicules de la marque Peugeot. Par ailleurs cette méthode possède deux variantes, dont l’une prend en compte un argument variable de type List au lieu d’un unique élément de type List.

V-C-1. cartesianProduct(List<? extends B> …lists)

Pour effectuer cette tâche de récupération des types de carburants, nous pouvons directement utiliser la deuxième variante de la méthode cartesianProduct(List<? extends B> …lists).

Méthode cartesianProduct(List<? extends List<? extends B>> ...lists)
Sélectionnez
1.
2.
3.
4.
5.
List<List<String>> immutableCartesianProduct = Lists.cartesianProduct(peugeotImmutableList, carburants);
out.println(immutableCartesianProduct);
/*
[[Peugeot 108, Essence], [Peugeot 108, Diesel], [Peugeot 108, GNV], [Peugeot 108, Hybride], [Peugeot 108, Biodiesel], [Peugeot 108, Electric], [Peugeot Nouvelle SUV 3008, Essence], [Peugeot Nouvelle SUV 3008, Diesel], [Peugeot Nouvelle SUV 3008, GNV], [Peugeot Nouvelle SUV 3008, Hybride], [Peugeot Nouvelle SUV 3008, Biodiesel], [Peugeot Nouvelle SUV 3008, Electric], [Peugeot Nouvelle SUV 5008, Essence], [Peugeot Nouvelle SUV 5008, Diesel], [Peugeot Nouvelle SUV 5008, GNV], [Peugeot Nouvelle SUV 5008, Hybride], [Peugeot Nouvelle SUV 5008, Biodiesel], [Peugeot Nouvelle SUV 5008, Electric], [Peugeot Traveller/Expert Combi, Essence], [Peugeot Traveller/Expert Combi, Diesel], [Peugeot Traveller/Expert Combi, GNV], [Peugeot Traveller/Expert Combi, Hybride], [Peugeot Traveller/Expert Combi, Biodiesel], [Peugeot Traveller/Expert Combi, Electric], [Peugeot Boxer, Essence], [Peugeot Boxer, Diesel], [Peugeot Boxer, GNV], [Peugeot Boxer, Hybride], [Peugeot Boxer, Biodiesel], [Peugeot Boxer, Electric], [Peugeot Tepee, Essence], [Peugeot Tepee, Diesel], [Peugeot Tepee, GNV], [Peugeot Tepee, Hybride], [Peugeot Tepee, Biodiesel], [Peugeot Tepee, Electric]]
*/

V-C-2. cartesianProduct(List<? extends List<? extends B>> lists)

Soit nous pouvons utiliser une boucle pour parcourir les couples formés par le produit cartésien souhaité [véhicule, carburant].

Méthode cartesianProduct(List<? extends List<? extends B>> lists)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
peugeotImmutableList.forEach(vehicule -> {
            carburants.stream()
                    //.filter(carburant -> carburant.equals("Diesel")) // si on souhaite uniquement les véhicules "Diesel"
                    .map(carburant -> ImmutableList.of(vehicule, carburant))
                    .forEachOrdered(couple -> {
                        out.println(couple);
                    });
        });

V-D. La méthode charactersOf

Maintenant nous souhaitons récupérer indépendamment chaque caractère d’une séquence de caractères d’un véhicule de la marque Peugeot.

V-D-1. charactersOf(CharSequence sequence)

Méthode charactersOf(CharSequence sequence)
Sélectionnez
1.
2.
3.
final CharSequence vehiculePeugeot4008 = "Peugeot 4008";
final List<Character> peugeot4008 = Lists.charactersOf(vehiculePeugeot4008);
out.println(peugeot4008); // [P, e, u, g, e, o, t,  , 4, 0, 0, 8]

Comme il ne s’agit que d’une vue de la séquence de caractères, cette dernière n’est accessible qu’en lecture seule. Par contre toute modification de la séquence de caractères initiale entraîne une modification de la vue.

UnsupportedOperationException
Sélectionnez
peugeot4008.remove(7); // UnsupportedOperationException

V-D-2. charactersOf(String string)

Nous pouvons effectuer la même opération pour récupérer indépendamment chaque caractère d’une chaîne de caractères (d’un véhicule de la marque Peugeot) passée en paramètre. Par contre avec cette surcharge de la méthode charactersOf la liste renvoyée est directement immuable.

Méthode ImmutableList<Character> charactersOf(String string)
Sélectionnez
1.
2.
final ImmutableList<Character> vehiculePeugeot5008 = Lists.charactersOf("Peugeot 5008");
out.println(vehiculePeugeot5008); // [P, e, u, g, e, o, t,  , 5, 0, 0, 8]

V-E. La méthode newArrayList

Nous voulons cette fois-ci instancier un type ArrayList mutable comme nous le faisons lorsque nous utilisons cette structure de données. Mais avec l’API de Google Guava cela se fait différemment.

V-E-1. newArrayList()

Méthode ArrayList<E> newArrayList()
Sélectionnez
1.
2.
3.
4.
5.
6.
final ArrayList<String> vehiculeMutableArrayList = Lists.newArrayList(); // une ArrayList mutable …
vehiculeMutableArrayList.add("Peugeot 207"); // à laquelle on ajoute un véhicule Peugeot
vehiculeMutableArrayList.add("Renault Velsatis"); // puis un véhicule Renault
vehiculeMutableArrayList.add("Citroen Saxo"); // puis un véhicule Renault
out.println(vehiculeMutableArrayList);
// [Peugeot 207, Renault Velsatis, Citroen Saxo]

V-E-2. newArrayList(E… elements)

Cette méthode possède également une autre variante prenant comme paramètre une ellipse ; cette variante quant à elle nous permet de créer une ArrayList directement avec nos véhicules.

Méthode ArrayList<E> newArrayList(E... elements)
Sélectionnez
1.
2.
3.
4.
final ArrayList<String> toyotaMutableArrayList = Lists.newArrayList("Toyota Starlet", "Toyota Nouvelle Starlet"); // une liste mutable initialisée avec des véhicules Toyota 
toyotaMutableArrayList.add("Toyota Nouvelle Starlet+"); // à laquelle on ajoute un nouveau véhicule Toyota
out.println(toyotaMutableArrayList);
// [Toyota Starlet, Toyota Nouvelle Starlet, Toyota Nouvelle Starlet+]

V-F. La méthode LinkedList

Idem avec pour un objet de type LinkedList :

Méthode LinkedList<E> newLinkedList()
Sélectionnez
1.
2.
3.
4.
5.
6.
final LinkedList<String> vehiculeMutableLinkedList = Lists.newLinkedList();  // une LinkedList mutable ...
vehiculeMutableLinkedList.add("Opel Zafira Life"); // à laquelle on ajoute un véhicule Opel
vehiculeMutableLinkedList.add("Citroen DS 7 Crossback"); // à laquelle on ajoute un véhicule Citroen
vehiculeMutableLinkedList.add("Renault Koléos"); // à laquelle on ajoute un véhicule Renault
out.println(vehiculeMutableLinkedList);
// [Opel Zafira Life, Citroen DS 7 Crossback, Renault Koléos]
Méthode LinkedList<E> newLinkedList(E... elements)
Sélectionnez
1.
2.
3.
4.
final LinkedList<String> opelMutableLinkedList = Lists.newLinkedList(Arrays.asList(opel));
opelMutableLinkedList.add("Opel Insignia Grand Sport");
out.println(opelMutableLinkedList);
// [Opel Calibra, Opel Ascona, Opel Corsa, Opel Alambra, Opel Meriva, Opel Insignia Grand Sport]

V-G. La méthode partition(List<T> list, int size)

L’API Google Guava nous permet également de partitionner (sectionner en sous-liste) notre liste de véhicules en choisissant la longueur de chacune des sous-listes. À noter que si tous les éléments ne peuvent pas être mis dans une sous-liste de longueur définie alors ils se retrouvent dans une sous-liste de longueur inférieure à celle définie à l’origine.

Méthode List<List<T>> partition(List<T> list, int size)
Sélectionnez
1.
2.
3.
4.
5.
6.
final List<List<String>> citroenListCarburants = Lists.partition(carburants, 5);
out.println(citroenListCarburants);
List<String> subList = Arrays.asList(citroen).subList(1, 3);
out.println(subList);
// [[Essence, Diesel, GNV, Hybride, Biodiesel], [Electric]]
// [Diesel, GNV]

V-H. La méthode reverse(List<T> list)

Désormais nous voulons restituer notre liste de véhicules dans l’ordre inverse d’insertion.

Méthode List<T> reverse(List<T> list)
Sélectionnez
1.
2.
3.
4.
5.
out.println(Arrays.asList(toyota));
final List<String> toyotaReverseList = Lists.reverse(Arrays.asList(toyota));
out.println(toyotaReverseList);
// [Toyota Auris, Toyota Nouvelle Yaris, Toyota Aygo X , Toyota Prius+, Toyota Nouvelle C-HR]
// [Toyota Nouvelle C-HR, Toyota Prius+, Toyota Aygo X , Toyota Nouvelle Yaris, Toyota Auris]

V-I. La méthode transform(List<F> fromList, Function<? super F,? extends T> function)

Nous pouvons également transformer les éléments de notre liste en leur appliquant une fonction de « transformation » prédéfinie.

Nous avons trouvé un procédé permettant de gagner 40 % sur la consommation du litre au 100 km de nos véhicules.

Méthode <F,T> List<T> transform(List<F> fromList, Function<? super F,? extends T> function)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
final Function<Float, Float> multiplier = t -> t * 0.6f;
// To use a java.util.function.Function in a context where a com.google.common.base.Function is needed, use function::apply. 
// donc on peut utiliser directement la méthode de référence (sans argument) apply
final List<Float> diminuerConsommation = Lists.transform(consommations, multiplier::apply);
out.println(diminuerConsommation); // 1.5600000381469727
// ou directement avec la classe String de l'API Java
final List<String> carburantsToUppercase = Lists.transform(carburants, String::toUpperCase);
out.println(carburantsToUppercase); 
// [2.5800002, 3.4800003, 4.1400003, 4.44, 5.1000004, 5.52]
// [ESSENCE, DIESEL, GNV, HYBRIDE, BIODIESEL, ELECTRIC]

VI. Conclusion

Nous avons fait le tour des méthodes utilitaires présentes dans la classe Lists. Comme vous avez pu le constater ces méthodes sont à la fois très simples d’utilisation et particulièrement efficaces. Je vous invite fortement à les implémenter dans vos projets, car elles vous apporteront des avantages sans précédent en termes de productivité.

Par ailleurs, je vous invite à créer un fil de discussion afin d’y laisser tous vos commentaires pour l’appréciation, voire pour l’amélioration des futurs tutoriels.

VII. Remerciements

Je tiens à remercier tout particulièrement claudeLELOUP pour la relecture orthographique de cet article.

Je remercie aussi la communauté developpez.com qui propose un forum d’entraide d’une richesse fortement appréciable. Je remercie également Mickael Baron qui donne de son temps pour s’occuper de la rubrique Java, RomeoBeni d’avoir écrit un ouvrage d’une rare simplicité pour apprendre à programmer en Java, mais aussi à tous les membres répondant aux questions des débutants en programmation dont je faisais partie il y a quelques années de cela. Merci à tous.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2021 RAUZDUEL Rony. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.