--- title: JAVA - 8. Les collections tags: POO --- ## Les collections Pour gérer un ensemble d’éléments, java nous propose l'utilisation des tableaux. c'est ce qu'on a couvert précédemment dans le cours. Bien que simple à mettre en œuvre, cette solution n’est pas très souple. Son principal défaut tient au **caractère fixe de la taille d’un tableau**. S’il n’y a plus de place pour stocker des éléments supplémentaires, il faut créer un nouveau tableau plus grand et y transférer le contenu du tableau précédent. Cette solution, lourde à mettre en œuvre, est consommatrice de ressources. En plus des tableaux, Java nous propose de nombreuses structures pour faciliter la gestion et la manipulation de grandes quantités d’informations. Elles sont regroupée sous le terme **collection**. Les **collections** sont regroupées dans une vaste palette (**Framework**) d’interfaces et de classes: - Les interfaces décrivent les fonctionnalités disponibles - Les classes implémentent et fournissent réellement ces fonctionnalités Suivant le mode de gestion des éléments souhaité, on utilisera la classe la plus adaptée. Cependant, les mêmes fonctionnalités de base doivent être accessibles quel que soit le mode de gestion (exemple : l'ajout d'éléments à une collection, la suppression ... etc. ) :::info Pour assurer la présence de ces fonctionnalités indispensables dans toutes les classes, celles-ci ont été regroupées dans l’interface **Collection** qui est par la suite utilisée comme interface de base. ::: Voici les avantages des collections: - **API cohérente**: l'API possède un ensemble d'interfaces de base telles que **Collection** ou **Map**, toutes les classes ( ArrayList , LinkedList , Vector , etc.) qui implémentent ces interfaces ont un ensemble commun de méthodes. - **Réduit l'effort de programmation** : un programmeur n'a pas à se soucier de la conception de la collection, mais il peut plutôt se concentrer sur sa meilleure utilisation dans son programme. Par conséquent, le concept de base de la programmation orientée objet qui est l'abstraction a été mis en œuvre avec succès. - **Augmente la vitesse et la qualité du programme** : augmente les performances en fournissant des implémentations avec de hautes performances de structures de données et d'algorithmes utiles, car dans ce cas, le programmeur n'a pas besoin de penser à la meilleure implémentation d'une structure de données spécifique. Il peut simplement utiliser la meilleure implémentation pour augmenter considérablement les performances de son programme. ## Hiérarchie de collection Framework Le package **java.util** contient toutes les classes et interfaces requises par l'infrastructure de collections La hiérarchie des interfaces et de classes est représentée sur le diagramme ci-dessous: ![](https://i.imgur.com/FuazeWf.png) L'interface **Collection** (java.util.Collection) et l'interface **Map** (java.util.Map) sont les deux principales **interfaces racines** des classes de collection Java. Toutes ces interfaces sont **génériques** afin de gérer des ensembles composés de n’importe quels types d’éléments tout en évitant les fastidieuses opérations de transtypage. Pour chacune de ces interfaces, une ou plusieurs classes correspondantes sont disponibles. *Ce sont bien sûr celles-ci qui vont nous intéresser* :::info L'interface **Collection** étend l'interface **Iterable**. Par conséquent, toutes ses interfaces dérivées implémentent cette interface. La fonctionnalité principale de l'interface **Iterable** est de fournir un **Iterator** pour les collections. ::: ## La classe ArrayList La classe **ArrayList** est une implémentation de l’interface **List**. Elle permet la gestion des éléments que l’on y place de manière quasi similaire à celle disponible avec un tableau, avec en plus l’**aspect dynamique**. Elle ajuste automatiquement sa capacité à mesure que vous ajoutez et supprimez des éléments, sans que vous n’ayez rien à faire. :::success L'interface **List** est une collection **ordonnée** (**ses elements sont indexés, la première position est à l’index 0**) et elle peut contenir des éléments dupliqués. ::: List hérite les méthodes de l'interface Collection, l'interface List inclut des opérations comme la manipulation des éléments, la recherche, le parcours avec Iterator. ### :small_blue_diamond: Création À la création d'un ArrayList, il est de mise de spécifier le type des objets qu'il contiendra ```java= public static void main(String[] args) { // un ArrayList de chaînes de caractères ArrayList<String> semaine = new ArrayList<String>(); // On peut typer l'objet par son interface List<String> mois = new ArrayList<String>(); // on suppose ici que la classe Foobar a déjà été déclarée ArrayList<Foobar> liste = new ArrayList<Foobar>(); } ``` :::info Pour des types primitifs, il faut utiliser les classes d'enrobage (**Wrapper**). ::: Par exemple, pour un ArrayList de valeurs de type **int**, on utilise la classe d'enrobage **Integer**. ```java= List<Integer> nombres = new ArrayList<Integer>(); ``` ### :small_blue_diamond: Ajouter des éléments On utilise la méthode **add**, qui ajoute l'élément à la fin de la liste. ```java= Foobar foo = new Foobar(); liste.add(foo); ``` Dans le cas des types primitifs, la conversion en une instance de la classe d'enrobage correspondante se fait automatiquement (c'est qu'on appelle l'**autoboxing**): ```java= // ajoute un Integer nombres.add(5); ``` Une version surchargée de cette méthode permet de choisir l’emplacement où les éléments sont placés. ```java= liste.add(2,foo); ``` ### :small_blue_diamond: Vérifier l'état de la liste - Pour savoir si la liste est vide, on utlise la méthode **isEmpty**. ```java= if(liste.isEmpty()){ System.out.println("Liste vide"); } ``` - Pour savoir combien d'éléments elle contient (**sa taille**), on utilise la méthode **size** ```java= System.out.println("La liste contient " + liste.size() + " éléments"); ``` ### :small_blue_diamond: Manipuler les éléments de la liste - Pour vérifier si un élément est contenu dans la liste, on utilise la méthode **contains** ```java= if(liste.contains(foo)){ System.out.println("Objet trouvé!"); } ``` - Pour trouver la position d'un élément, on utilise la méthode **indexOf**. Cette méthode retourne **-1** si non trouvé. ```java= System.out.println( "L'objet est à la position " + liste.indexOf(foo)); ``` - Pour obtenir (sans le retirer) un élément situé à une position donnée, on utilise la méthode **get**. ```java= // obtient le 6e élément Foobar f = liste.get(5); ``` - Pour retirer un élément de la liste, on utilise la méthode **remove**. ```java= liste.remove(foo); ``` Si le même objet est plusieurs fois dans la liste, c'est la première occurence qui est supprimée. ### :small_blue_diamond: Parcourir une liste Le mécanisme d'itération permet de parcourir séquentiellement tous les éléments d'une liste, de manière uniforme et efficace. Le parcours des éléments de la liste est possible avec une boucle **for**, **for each** ou en utilisant l’objet **Iterator** fourni par la méthode **iterator()** de la classe **Arraylist**. - **Avec une boucle for** ```java= public static void main (String[] args){ // Création d'une collection de type List List<String> couleurs = ArrayList<String>(); // Ajout des éléments couleurs.add("Bleu"); couleurs.add("Rouge"); couleurs.add("vert"); // boucle for for (int i = 0; i < couleurs.size(); i++) { // Récupération de l'element à l'aide de la méthode get et son index System.out.println(couleurs.get(i)); } } ``` - **Avec la boucle for-each** La boucle **for-each** (appelée **boucle for avancée**) a été intégrée depuis Java 5. Elle est utilisée pour parcourir les tableaux ainsi que les collections d'objets. La boucle **for-each** est utilisée afin de prendre une par une les valeurs de la liste et la stocker à son tour dans la variable locale avant de la manipuler en se servant de cette variable. ```java= public static void main (String[] args){ // Création d'une liste grâce à la méthode staticasList de Arrays List<String> couleurs = Arrays.asList("Bleu", "Rouge", "vert"); // Pour tout élément str de couleurs for (String str : couleurs) { System.out.println(str); } } ``` - **Avec un Iterator** En Java, toute collection possède une méthode **iterator()** qui retourne un itérateur permettant d'accéder aux éléments un par un en **avançant** dans la collection. Initialement, l'itérateur **est placé avant le premier élément**. A chaque accès, le prochain élément est retourné et l'itérateur avance à l'élément suivant. Les deux méthodes principales d'un itérateur sont : - **hasNext()** qui retourne true s'il reste des éléments dans l'itération. - **next()** qui retourne le prochain élément et avance dans l'itération. S'il n'y a plus d'élément, une **NoSuchElementException** est levée. ```java= public static void main (String[] args){ // Création d'une collection de type List List<String> couleurs = Arrays.asList("Bleu", "Rouge", "vert"); // Crée un nouvel itérateur sur la liste, initialisé AVANT le 1er élément Iterator<String> it = couleurs.iterator(); //Tant qu'il reste des éléments while (it.hasNext()) { // Récupère le prochain élément et avance String str = it.next(); System.out.println(str); } } ``` ## La classe HashSet **HashSet** implémente l'interface **java.util.Set**. La classe **HashSet** crée une collection d'objets **non-ordonée** qui utilise une **table de hachage** pour le stockag qui est un très bon atout pour les haute performances, mais il ne garantie pas l'ordre d'insertion des éléments. Une table de hachage stocke les valeurs en leurs donnant une clé unique pour les identifier. Une clé ne peut pas être associée à deux valeurs contrairement à HashMap. Une collection de type **Set** est la représentation du modèle mathématique des ensembles en java. l'interface **Set** hérite les méthodes de l'interface **Collection** et ajoute la propriété de l'interdiction des éléments en double qui doivent exister qu'une seule fois. :::success Une collection de type **Set** ne permet pas l'ajout de doublons ni l'accès direct à un élément de la collection. ::: ### :small_blue_diamond: Création À la création d'un HashSet, il est de mise de spécifier le type des objets qu'il contiendra ```java= public static void main(String[] args) { // un HashSet de chaînes de caractères HashSet<String> hset = new HashSet<String>(); // On peut typer l'objet par son interface Set<String> mois = new HashSet<String>(); } ``` :::info Pour des types primitifs, il faut utiliser les classes d'enrobage (**Wrapper**). ::: ### :small_blue_diamond: Ajouter des éléments On utilise la méthode **add** ```java= //ajouter des éléments hset.add("hôtel"); hset.add("motel"); hset.add("fondouk"); hset.add("ressort");; ``` L'invocation de la méthode add() avec en paramètre un élément déjà présent dans la collection n'aucun effet. ### :small_blue_diamond: Vérifier l'état d'un Set - Pour savoir si l'ensemble (Set) est vide, on utlise la méthode **isEmpty**. ```java= if(hset.isEmpty()){ System.out.println("Set vide"); } ``` - Pour savoir combien d'éléments il contient (**sa taille**), on utilise la méthode **size** ```java= System.out.println("Le set contient " + hset.size() + " éléments"); ``` ### :small_blue_diamond: Manipuler les éléments d'un Set - Pour vérifier si un élément est contenu dans le set, on utilise la méthode **contains** ```java= //tester l'existance System.out.println(hset.contains("motel")); ``` - Pour retirer un élément de le set, on utilise la méthode **remove**. ```java= //supprimer l'objet motel hset.remove("motel"); ``` ### :small_blue_diamond: Parcourir les éléments d'un Set - **Avec la boucle for-each** ```java= public static void main (String[] args){ //parcourir HashSet for(String valeur:hset){ System.out.println(valeur); } } ``` - **Avec un Iterator** Il est possible d’utiliser un Iterator pour parcourir les éléments d'un set. ```java= public static void main (String[] args){ // Crée un nouvel itérateur sur le set, initialisé AVANT le 1er élément Iterator<String> it = hset.iterator(); //Tant qu'il reste des éléments while (it.hasNext()) { // Récupère le prochain élément et avance String str = it.next(); System.out.println(str); } } ``` ## La classe HashMap Un **HashMap** ou **dictionnaire** permet de manipuler des couples **clé/valeur**. C'est une **collection de valeurs** auxquelles on peut accéder instantanément au moyen d’une **clé unique**. La **clé** et la **valeur** peuvent être de n’importe quel type. La classe **HashMap** implémente l’interface **Map**. C’est un type de collection qui diffère sensiblement des autres types de collections. Cette classe est utile si vous avez besoin de manipuler des éléments en fonction d’une clé. ### :small_blue_diamond: Création d'un dictionnaire L’exemple suivant propose un objet (dictionnaire) permettant de manipuler des **couples Integer/String**, la **clé** étant de **type Integer** et la **valeur** étant de **type String**. ```java= public static void main(String[] args) { Map<Integer, String> departements = new HashMap<Integer, String>(); } ``` ### :small_blue_diamond: Ajouter des éléments d'un dictionnaire La méthode **put** permet d’ajouter un couple **clé/valeur**. ```java= departements.put(35, "Ille Et Vilaine"); departements.put(29, "Fin"); //Correction par écrasement: departements.put(29, "Finistère"); departements.put(22,"Côtes d'Armor"); ``` La **clé** étant **unique, si elle est déjà utilisée, la méthode **put** écrase purement et simplement le couple **clé/valeur** déjà présent. ### :small_blue_diamond: Manipuler les éléments d'un dictionnaire - La méthode **remove** permet de supprimer une entrée. Elle attend un paramètre correspondant à la clé à supprimer. ```java= //Suppression d'une entrée departements.remove(22); ``` - La méthode **get** permet d’obtenir la **valeur** en lien avec la **clé** passée en paramètre. ```java= String dep35 = departements.get(35); System.out.println("Le nom du département 35 est : " + dep35); ``` - Les méthodes **containsKey** et **containsValue** permettent de vérifier respectivement la présence d’une clé et la présence d’une valeur en utilisant leur méthode equals. ```java= //Recherche d'un élément par la clé if(departements.containsKey(29)){ System.out.println("Le département 29 est référencé"); }else{ System.out.println("Le département 29 n'est pas référencé"); } //Recherche d'un élément par la valeur if(departements.containsValue("Morbihan")){ System.out.println("oui"); }else{ System.out.println("non"); } ``` ### :small_blue_diamond: Parcourir les éléments d’un HashMap Un ensemble de méthodes permet de parcourir les clés (**keySet**), les valeurs (**values**) ou les couples clé/valeur (**entrySet**). - **Parcourir la collection de valeurs** ```java= //Parcourir la collection de valeurs for(String valeur:departements.values()){ System.out.println("-"+valeur); } ``` - **Parcourir la collection de clés** ```java= //Parcourir la collection de clés //c'est un Set car chaque clé est unique for(int cle:departements.keySet()){ System.out.println("-"+cle); } ``` - **Parcourir la collection de clé/valeur** ```java= //Parcourir la collection de clé/valeur //c'est un Set car chaque clé est unique for(Entry<Integer, String> entree : departements.entrySet()){ System.out.println(entree.getKey()+" : "+entree.getValue()); } ```