Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !
Apprenez à programmer en Java

Apprenez à programmer en Java

Mis à jour le jeudi 19 juin 2014
  • 6 semaines
  • Moyen

Voici un chapitre qui va particulièrement vous plaire. Nous allons voir que nous ne sommes pas obligés de stocker nos données dans des tableaux ! Ces fameuses collections d'objets sont d'ailleurs dynamiques : en gros, elles n'ont pas de taille prédéfinie. Il est donc impossible de dépasser leur capacité !

Je ne passerai pas en revue tous les types et tous les objets Collection car ils sont nombreux, mais nous verrons les principaux d'entre eux. Les objets que nous allons aborder ici sont tous dans le packagejava.util. Facile à retenir, non ?

Ce chapitre vous sera d'une grande utilité, car les collections sont primordiales dans les programmes Java.

Les différents types de collections

Avant de vous présenter certains objets, je me propose de vous présenter la hiérarchie d'interfaces composant ce qu'on appelle les collections. Oui, vous avez bien lu, il s'agit bien d'interfaces : celles-ci encapsulent la majeure partie des méthodes utilisables avec toutes les implémentations concrètes. Voici un petit diagramme de classes sur la figure suivante schématisant cette hiérarchie.

Hiérarchie d'interfaces
Hiérarchie d'interfaces

Vous pouvez voir qu'il existe plusieurs types de collections, que les interfaces List et Set implémentent directement l'interface Collection et que l'interface Map gravite autour de cette hiérarchie, tout en faisant partie des collections Java.

En lisant la suite de ce chapitre, vous constaterez que ces interfaces ont des particularités correspondant à des besoins spécifiques. Les objets de type List servent à stocker des objets sans condition particulière sur la façon de les stocker. Ils acceptent toutes les valeurs, même les valeurs null. Les types Set sont un peu plus restrictifs, car ils n'autorisent pas deux fois la même valeur (le même objet), ce qui est pratique pour une liste d'éléments uniques, par exemple. Les Map sont particulières, car elles fonctionnent avec un système clé - valeur pour ranger et retrouver les objets qu'elles contiennent.

Maintenant que je vous ai brièvement expliqué les différences entre ces types, voyons comment utiliser ces objets.

Les objets List

Les objets appartenant à la catégorie List sont, pour simplifier, des tableaux extensibles à volonté. On y trouve les objets Vector, LinkedList et ArrayList. Vous pouvez y insérer autant d'éléments que vous le souhaitez sans craindre de dépasser la taille de votre tableau. Ils fonctionnent tous de la même manière : vous pouvez récupérer les éléments de la liste via leurs indices. De plus, les List contiennent des objets. Je vous propose de voir deux objets de ce type qui, je pense, vous seront très utiles.

L'objet LinkedList

Une liste chaînée (LinkedList en anglais) est une liste dont chaque élément est lié aux éléments adjacents par une référence à ces derniers. Chaque élément contient une référence à l'élément précédent et à l'élément suivant, exceptés le premier, dont l'élément précédent vaut null, et le dernier, dont l'élément suivant vaut également null.

La figure suivante représente un un schéma qui vous permettra de mieux vous représenter le fonctionnement de cet objet :

Fonctionnement de la LinkedList
Fonctionnement de la LinkedList

Voici un code pour appuyer mes dires :

import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
 
public class Test {
 
  public static void main(String[] args) {
    List l = new LinkedList();
    l.add(12);
    l.add("toto ! !");
    l.add(12.20f);

    for(int i = 0; i < l.size(); i++)
      System.out.println("Élément à l'index " + i + " = " + l.get(i));
  }
}

Si vous essayez ce code, vous constaterez que tous les éléments s'affichent !

Il y a autre chose que vous devez savoir sur ce genre d'objet : ceux-ci implémentent l'interface Iterator. Ainsi, nous pouvons utiliser cette interface pour lister notre LinkedList.

Un itérateur est un objet qui a pour rôle de parcourir une collection. C'est d'ailleurs son unique raison d'être. Pour être tout à fait précis, l'utilisation des itérateurs dans Java fonctionne de la même manière que le pattern du même nom. Tout comme nous avons pu le voir avec la pattern strategy, les design patterns sont en fait des modèles de conception d'objets permettant une meilleure stabilité et une réutilisabilité accrue. Les itérateurs en font partie.

Dans le code suivant, j'ai ajouté le parcours avec un itérateur :

import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
 
public class Test {
 
  public static void main(String[] args) {
    List l = new LinkedList();
    l.add(12);
    l.add("toto ! !");
    l.add(12.20f);

    for(int i = 0; i < l.size(); i++)
      System.out.println("Élément à l'index " + i + " = " + l.get(i));

      System.out.println("\n \tParcours avec un itérateur ");
      System.out.println("-----------------------------------");
      ListIterator li = l.listIterator();

      while(li.hasNext())
        System.out.println(li.next());
  }
}

Les deux manières de procéder sont analogues !

Attention, je dois vous dire quelque chose sur les listes chaînées : vu que tous les éléments contiennent une référence à l'élément suivant, de telles listes risquent de devenir particulièrement lourdes en grandissant ! Cependant, elles sont adaptées lorsqu'il faut beaucoup manipuler une collection en supprimant ou en ajoutant des objets en milieu de liste. Elles sont donc à utiliser avec précaution.

L'objet ArrayList

Voici un objet bien pratique. ArrayList est un de ces objets qui n'ont pas de taille limite et qui, en plus, acceptent n'importe quel type de données, y compris null ! Nous pouvons mettre tout ce que nous voulons dans un ArrayList, voici un morceau de code qui le prouve :

import java.util.ArrayList;
 
public class Test {
 
  public static void main(String[] args) {
         
    ArrayList al = new ArrayList();
    al.add(12);
    al.add("Une chaîne de caractères !");
    al.add(12.20f);
    al.add('d');
                
    for(int i = 0; i < al.size(); i++)
    {
      System.out.println("donnée à l'indice " + i + " = " + al.get(i));
    }               
  }
}

Si vous exécutez ce code, vous obtiendrez la figure suivante.

Parcours d'un ArrayList
Parcours d'un ArrayList

Je pense que vous voyez déjà les avantages des ArrayList.
Sachez aussi qu'il existe tout un panel de méthodes fournies avec cet objet :

  • add() permet d'ajouter un élément ;

  • get(int index) retourne l'élément à l'indice demandé ;

  • remove(int index) efface l'entrée à l'indice demandé ;

  • isEmpty() renvoie « vrai » si l'objet est vide ;

  • removeAll() efface tout le contenu de l'objet ;

  • contains(Object element) retourne « vrai » si l'élément passé en paramètre est dans l'ArrayList.

Contrairement aux LinkedList, les ArrayList sont rapides en lecture, même avec un gros volume d'objets. Elles sont cependant plus lentes si vous devez ajouter ou supprimer des données en milieu de liste. Pour résumer à l'extrême, si vous effectuez beaucoup de lectures sans vous soucier de l'ordre des éléments, optez pour une ArrayList ; en revanche, si vous insérez beaucoup de données au milieu de la liste, optez pour une Linkedlist.

Les objets Map

Une collection de type Map est une collection qui fonctionne avec un couple clé - valeur. On y trouve les objets Hashtable, HashMap, TreeMap, WeakHashMap… La clé, qui sert à identifier une entrée dans notre collection, est unique. La valeur, au contraire, peut être associée à plusieurs clés.

Ces objets ont comme point faible majeur leur rapport conflictuel avec la taille des données à stocker. En effet, plus vous aurez de valeurs à mettre dans un objet Map, plus celles-ci seront lentes et lourdes : logique, puisque par rapport aux autres collections, il stocke une donnée supplémentaire par enregistrement. Une donnée c'est de la mémoire en plus et, même si les ordinateurs actuels en ont énormément, gardez en tête que « la mémoire, c'est sacré » (je vous rappelle que les applications Java ne sont pas forcément destinées aux appareils bénéficiant de beaucoup de mémoire).

L'objet Hashtable

Vous pouvez également dire « table de hachage », si vous traduisez mot à mot… On parcourt cet objet grâce aux clés qu'il contient en recourant à la classe Enumeration. L'objet Enumeration contient notre Hashtable et permet de le parcourir très simplement. Regardez, le code suivant insère les quatre saisons avec des clés qui ne se suivent pas, et notre énumération récupère seulement les valeurs :

import java.util.Enumeration;
import java.util.Hashtable;

public class Test {

  public static void main(String[] args) {

    Hashtable ht = new Hashtable();
    ht.put(1, "printemps");
    ht.put(10, "été");
    ht.put(12, "automne");
    ht.put(45, "hiver");

    Enumeration e = ht.elements();

    while(e.hasMoreElements())
      System.out.println(e.nextElement());

  }
}

Cet objet nous offre lui aussi tout un panel de méthodes utiles :

  • isEmpty() retourne « vrai » si l'objet est vide ;

  • contains(Object value) retourne « vrai » si la valeur est présente. Identique à containsValue(Object value) ;

  • containsKey(Object key) retourne « vrai » si la clé passée en paramètre est présente dans la Hashtable ;

  • put(Object key, Object value) ajoute le couple key - value dans l'objet ;

  • elements() retourne une énumération des éléments de l'objet ;

  • keys() retourne la liste des clés sous forme d'énumération.

De plus, il faut savoir qu'un objet Hashtable n'accepte pas la valeur null et qu'il est Thread Safe, c'est-à-dire qu'il est utilisable dans plusieurs threads (cela signifie que plusieurs éléments de votre programme peuvent l'utiliser simultanément ; nous y reviendrons) simultanément sans qu'il y ait un risque de conflit de données.

L'objet HashMap

Cet objet ne diffère que très peu de la Hashtable:

  • il accepte la valeur null ;

  • il n'est pas Thread Safe.

En fait, les deux objets de type Map sont, à peu de choses près, équivalents.

Les objets Set

Un Set est une collection qui n'accepte pas les doublons. Par exemple, elle n'accepte qu'une seule fois null, car deux valeurs null sont considérées comme un doublon. On trouve parmi les Set les objets HashSet, TreeSet, LinkedHashSet… Certains Set sont plus restrictifs que d'autres : il en existe qui n'acceptent pas null, certains types d'objets, etc.

Les Set sont particulièrement adaptés pour manipuler une grande quantité de données. Cependant, les performances de ceux-ci peuvent être amoindries en insertion. Généralement, on opte pour un HashSet, car il est plus performant en temps d'accès, mais si vous avez besoin que votre collection soit constamment triée, optez pour un TreeSet.

L'objet HashSet

C'est sans nul doute la plus utilisée des implémentations de l'interface Set. On peut parcourir ce type de collection avec un objet Iterator ou extraire de cet objet un tableau d'Object :

import java.util.HashSet;
import java.util.Iterator;
 
public class Test { 
  public static void main(String[] args) {         
    HashSet hs = new HashSet();
    hs.add("toto");
    hs.add(12);
    hs.add('d');

    Iterator it = hs.iterator();
    while(it.hasNext())
      System.out.println(it.next());
 
    System.out.println("\nParcours avec un tableau d'objet");
    System.out.println("-----------------------------------");
                
    Object[] obj = hs.toArray();
    for(Object o : obj)
      System.out.println(o);                
  }
}

Voici une liste des méthodes que l'on trouve dans cet objet :

  • add() ajoute un élément ;

  • contains(Object value) retourne « vrai » si l'objet contient value ;

  • isEmpty() retourne « vrai » si l'objet est vide ;

  • iterator() renvoie un objet de type Iterator ;

  • remove(Object o) retire l'objet o de la collection ;

  • toArray() retourne un tableau d'Object.

Voilà ! Nous avons vu quelque chose d'assez intéressant que nous pourrons utiliser dans peu de temps, mais avant, nous avons encore du pain sur la planche. Dans le chapitre suivant nous verrons d'autres aspects de nos collections.

  • Une collection permet de stocker un nombre variable d'objets.

  • Il y a principalement trois types de collection : les List, les Set et les Map.

  • Chaque type a ses avantages et ses inconvénients.

  • Les Collection stockent des objets alors que les Map stockent un couple clé - valeur.

  • Si vous insérez fréquemment des données en milieu de liste, utilisez une LinkedList.

  • Si vous voulez rechercher ou accéder à une valeur via une clé de recherche, optez pour une collection de type Map.

  • Si vous avez une grande quantité de données à traiter, tournez-vous vers une liste de type Set.

Découvrez aussi ce cours en...

Exemple de certificat de réussite
Exemple de certificat de réussite