Les collections en Java permettent de gérer des ensembles d’objets.
Java propose par défaut une bibliothèque de classes permettant de couvrir l’essentiel des besoins d’un programmeur pour gérer des ensembles.
Pour commencer, nous allons étudier la gestion de liste d’objets (List/ArrayList), mais nous verrons plus tard qu’il est possible d’utiliser d’autres types d’ensemble.
Nous allons également découvrir l’utilité des Generics dans l’usage des collections, ainsi que l’Autoboxing/Unboxing.
Un premier type de collection très utilisé est List.
Il permet de traiter un ensemble d’objets sous la forme de liste.
Une liste est ordonnée.
Le nombre d’objets distinct peut être différent du nombre d’enregistrement.
java.util.List est une interface qui propose les fonctions suivantes (non exhaustif).
public int size() : détermine le nombre d’éléments dans la liste (et donc l’index maximal auquel on peut accéder)public E get(int index) : récupère l’élément positionné à l’index donné en paramètrepublic boolean contains(Object o): indique si oui ou non l’objet passé en paramètre est contenu dans la liste.public int indexOf(Object o) : indique l’index où se positionne l’objet passé en paramètre dans la liste.public void add(E e) : ajoute un élément de type E dans la listepublic E remove(int index) : supprime un élément de type E dans la liste et renvoie cet élémentpublic boolean remove(Object o) : supprime la première occurrence trouvé de l’élément passé en paramètre de la listeVous aurez remarqué le type de retour de get(int). E est le type d’objet contenu par la liste. Nous allons voir à quoi cela correspond dans le paragraphe suivant.
L’interface List ne peut être instanciée directement.
java.util.ArrayList est une classe (concrète) qui implémente cette interface et qui peut être instanciée.
Une List, par essence, permet de stocker n’importe quel objet. Ce sont donc des instances d’Object qui sont stockés dans une liste.
Il serait peu pratique de devoir spécifier à chaque appel des méthodes get(int), remove(int) le type de l’objet retourné.
Dans les version antérieures à la version 1.5 (ou 5.0) de Java, il était nécessaire d’écrire ceci:
public class AvantLesGenerics{
List list = new ArrayList();
public void init(){
Date date = new Date(11, 12, 2013);
list.add(date);
}
public void testSiPremiereDateValide(){
Date d = (Date)list.get(0); //Transtypage indispensable
System.out.println(d.estValide());
}
}En l’absence de transtypage, le compilateur indiquait une erreur, car la classe par défaut de List est Object et non Date.
Ce type de programmation était lourd. En effet, à la place de :
il serait plus simple d’écrire:
Ceci est possible depuis la version 1.5 de Java :
GenericsJuste après la déclaration du type (List), il est possible d’indiquer le type d’objet qu’elle contient.
Pour cela, il suffit de noter le type entre chevrons < et > :
Cette déclaration indique que la liste contiendra des objets de type Date.
List <Date>.
Une fois le type déclaré pour l’interface List, il n’est pas utile de le rappeler lors de l’instanciation.
Vous noterez que ArrayList est suivi d’un diamant (diamond operator): deux chevrons accolés <>.
Ceci permet au compilateur de savoir que le type générique correspondra à celui de la variable telle que déclarée précédemment
Il était obligatoire d’écrire explicitement le type lors de l’appel au constructeur avant la version Java SE 7:
la version du code qui compilait dans ce cas était:
List<Date> list = new ArrayList<Date>();
Ce n’est plus nécessaire depuis la version 7.0.
public class AvecLesGenerics{
List<Date> list = new ArrayList<>();
public void init(){
Date date = new Date(11,12,2013);
list.add(date);
}
public void testSiPremiereDateValide(){
System.out.println(list.get(0).estValide());
}
}Nous disposons donc d’un outil puissant et versatile pour stocker des listes d’objets.
Cette notion de Generics reste à approfondir mais ce que nous venons de voir va nous suffire dans un premier temps.
L’intérêt des collections est qu’elles permettent également de gérer des types primitifs.
En effet, même si ces types ne sont pas des instances, ils vont pouvoir être encapsulés dans des objets.
Ainsi, on aura la possibilité de stocker un int dans un objet de type Integer.
Observez bien le code suivant:
public class IntBoxing {
List<Integer> listeInt = new ArrayList<>();
public void init(){
int monNombrePrimitif = 24;
listeInt.add(monNombrePrimitif);
}
public void faire(){
System.out.println(listeInt.get(0) + 12);
}
public static void main(String[] args) {
IntBoxing ib = new IntBoxing();
ib.init();
ib.faire();
}
}Il est parfaitement fonctionnel.
Il vous est possible d’utiliser les collections pour stocker les types primitifs suivants en utilisant les réceptacles associés :
| Type primitif | Classe réceptacle |
|---|---|
| boolean | Boolean |
| byte | Byte |
| char | Character |
| float | Float |
| int | Integer |
| long | Long |
| short | Short |
| double | Double |
Ces réceptacles sont des objets boîtes (box) dans lesquelles sont stockées les valeurs primitives afin qu’elles soient prises en charge par les collections comme des objets.
Il est possible de parcourir une liste de deux manières avec une boucle for:
for(int i = 0 ; i < l.size() ; i++)for (Date date : listeDate)Nous avons utilisé l’interface List avec la classe ArrayList. Mais il est possible de l’interchanger avec une autre classe sans effet sur le reste du code.
Par exemple avec la classe LinkedList:
public class IntBoxing {
List<Integer> listeInt = new LinkedList<>();
public void init(){
int monNombrePrimitif = 24;
listeInt.add(monNombrePrimitif);
}
//... (le reste du code est identique à l'exemple précédent
}La classe LinkedList est une liste chaînée. Selon le cas, son mode de fonctionnement sera plus performant que si on utilise une ArrayList.
Vous pouvez tester cela avec le code suivant:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class PerformancesList {
public static final int MAX_VALUE = 100000;
List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();
public void insert(List<Integer> liste, int maxValue) {
for (int i = 0; i < maxValue; i++) {
liste.add(0, i);
}
}
public void parcours(List<Integer> liste, int maxValue) {
for (int i = 0; i < maxValue; i++) {
Integer integer = liste.get(i);
}
}
public static void main(String[] args) {
PerformancesList pl = new PerformancesList();
pl.insert(pl.arrayList, 10);
pl.insert(pl.linkedList, 10);
System.out.println("-- Insertion");
long debutOp = System.nanoTime();
pl.insert(pl.linkedList, MAX_VALUE);
long finOp = System.nanoTime();
System.out.println("Linked :" + (finOp - debutOp) / 1000000 + "ms");
debutOp = System.nanoTime();
pl.insert(pl.arrayList, MAX_VALUE);
finOp = System.nanoTime();
System.out.println("Array :" + (finOp - debutOp) / 1000000 + "ms");
pl.parcours(pl.arrayList, 10);
pl.parcours(pl.linkedList, 10);
System.out.println("-- Parcours");
debutOp = System.nanoTime();
pl.parcours(pl.linkedList, MAX_VALUE);
finOp = System.nanoTime();
System.out.println("Linked :" + (finOp - debutOp) / 1000000 + "ms");
debutOp = System.nanoTime();
pl.parcours(pl.arrayList, MAX_VALUE);
finOp = System.nanoTime();
System.out.println("Array :" + (finOp - debutOp) / 1000000 + "ms");
}
}Que constatez-vous à l’exécution de ce code ?
Remplissez une liste avec des entiers numérotés de 1 à 10.
Utilisez les deux types de boucle for pour parcourir une List d’entiers (de 1 à 20).
Le diagramme suivant donne une vision globale des Collections en Java

On s’aperçoit que List est un sous-type de Collection, tout comme Set ou Queue.
Nous allons ici nous intéresser sur certaines fonctionnalités de base de ces classes Set et Queue.
Vous pouvez trouver une documentation plus complète sur les collections ici
L’interface Set permet de contenir un ensemble d’objets. Cet ensemble ne contient pas de doublon (un objet y est stocké une et une seule fois).
Set se base sur la méthode equals pour s’assurer qu’aucun doublon n’est inséré dans la collection.Set n’aura aucun effet.equals renvoie true pour un des éléments du Set, n’entraînera pas sa présence en doublon.package com.example.collections;
import java.util.Set;
import java.util.TreeSet;
public class ExempleSet {
public static void main(String[] args) {
Set<Integer> entiers = new TreeSet<>();
entiers.add(1);
entiers.add(2);
entiers.add(3);
entiers.add(2);
entiers.add(3);
entiers.add(4);
entiers.remove(3);
System.out.println(entiers);
}
}TreeSet
Set dont les éléments sont ordonnésSortedSetHashSet
Set dont les éléments ne sont pas ordonnésLes sous-classes de l’interface Queue permettent de gérer des files d’attentes (de type FIFO ou LIFO).
Deux façons d’utiliser un objet de type Queue
| Type d’opération | Lance une exception | Renvoie une valeur spéciale |
|---|---|---|
| Insert | add(elem) | offer(elem) |
| Remove | remove() | poll() |
| Examine | element() | peek() |
package com.example.collections;
import java.util.LinkedList;
import java.util.Queue;
public class ExempleQueue {
public static void main(String[] args) {
int nombreClients = 0 ;
Queue<String> queue = new LinkedList<>();
while (nombreClients < 10){
queue.add("Client("+nombreClients+")");
nombreClients++;
}
while (!queue.isEmpty()) {
System.out.println(queue.remove());
}
}
}Une Map permet de stocker des éléments en les référençant via une clef d’accès.
C’est l’équivalent d’un dictionnaire. Pour trouver la définition d’un mot, on cherche le mot en question. Une fois trouvé, il est possible d’en lire la définition.
L’utilisation des Map permet de stocker et récupérer facilement une information, un pointeur ou un objet associé à un autre objet.
Une Map va associer
Date)PersonneMap <Date, Personne> personneParAnniversaireimport java.util.Map;
import java.util.HashMap;
public class Anniversaire {
private Map<String, Personne> anniv = new HashMap<>();
public void init(){
anniv.put("19/05/1955", new Personne("Gosling", "James"));
anniv.put("09/09/1941", new Personne("Ritchie", "Dennis"));
anniv.put("04/02/1943", new Personne("Thompson", "Ken"));
}
public void affichePrenom(String date){
System.out.println(anniv.get(date).prenom);
}
public static void main(String[] args) {
Anniversaire a = new Anniversaire();
a.init();
a.affichePrenom("04/02/1943");
}
}
class Personne{
String nom;
String prenom;
Personne(String nom, String prenom){
this.nom = nom;
this.prenom = prenom;
}
}On peut utiliser à la place de HashMap un type TreeMap ou EnumMap ainsi que d’autres classes (voir la Javadoc correspondante)
Les différences d’implémentation entre ces différentes classes ne seront pas discutées ici.
Parmi les fonctionnalités communes aux collections, il y a la possibilité de parcourir la liste des éléments d’une collection via l’interface Iterator.
Map est ici un peu à part. Elle est liée aux collections dans le fait qu’elle possède deux méthodes qui renvoient des instances de l’interface Set:
Set<K> keySet(): ensemble des clefsSet<Map.Entry<K,V>> entrySet() : ensemble des entréesÀ partir de ces instances de Set il est possible de récupérer un Iterator pour en parcourir les éléments de la Map.
Voici un exemple d’utilisation d’un Iterator. Ceci est applicable à toute sous-interface de Collection:
public void parcourir(Collection collection) {
Iterator it = collection.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}package com.example.collections;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class ExempleIteration {
public static void main(String[] args) {
Set<Integer> entiers = new TreeSet<>();
entiers.add(1);
entiers.add(2);
entiers.add(3);
entiers.add(4);
entiers.remove(3);
entiers.add(16);
entiers.add(8);
System.out.println("For sur Iterator");
for (Iterator<Integer> iterator = entiers.iterator(); iterator.hasNext();) {
Integer entier = iterator.next();
System.out.println(entier);
}
System.out.println("For-each");
for (Integer entier : entiers) {
System.out.println(entier);
}
}
}Nous allons cette fois gérer des listes de Vehicule (en créant une nouvelle classe Vehicule dans le paquet com.example.gestionflotte).
Cette classe comportera les attributs permettants de stocker les informations suivantes:
D’un point de vue administratif, une personne (physique ou morale) est propriétaire d’un ou plusieurs véhicules. Ces véhicules peuvent être des types suivants (le nombre de type est ici limité pour simplifier le programme) :
Une entreprise doit gérer une flotte de véhicule ayant ces différents types.
Pour chacun des points demandés, tester le code à partir la méthode main de la classe GestionFlotte créée plus bas.
Il n’est pas demandé de gérer l’ajout ou l’affichage par des commandes au clavier.
Le test du programme se fera en ajoutant “en dur” différents véhicules et en examinant la sortie (console) qu’il fournit.
Le programme doit s’exécuter entièrement automatiquement et afficher des messages sur chaque instruction en cours d’exécution.
Création des types de véhicules
Vehicule et faite en sorte qu’un identifiant unique soit associé à chaque nouvelle instance.VehiculeLeger, VehiculeLourd, VehiculeTransportPassagers)VehiculeTransportPassagers, ajouter un attribut indiquant le nombre maximal de passagers nommé nombreMaxPassagersVehiculeLeger, indiquer le type: entreprise (voiture à deux places), utilitaire ou particulier (véhicule à 5 places).equals qui permet de vérifier si deux véhicules sont identiques (même marque, même modèle). Ils peuvent avoir des identifiants différents.toString de la classe véhicule pour afficher l’identifiant, la marque et le modèle sous cette forme: "[134,marque,modèle]"On souhaite pouvoir gérer les listes suivantes:
listeGlobalelisteTransportPassagerslisteVehiculesLegersLa classe GestionFlotte va comporter une méthode main.
GestionFlotte, ajouter ces trois listes. Il ne doit pas être possible d’ajouter un type incorrect dans les différentes listes (pas de Véhicule Léger dans la liste des véhicules de transport de passagers).ajouterVehicule(Vehicule v)supprimerVehicule(Vehicule v)getNombreVehicules()Dans cette partie, il s’agit d’afficher les listes de véhicules sous forme de chaînes de caractères.
- `getListeInformationsTousVehicules()`
- `getListeInformationsVehiculesLegers()`
- `getListeInformationsVehiculesTransportsPassagers()`
- `synchroniserListesSpecialisees()`
ajouterVehicule(Vehicule v)).Reprendre les classes permettant la gestion d’une flotte de véhicules (GestionFlotte).
GestionFlotte un attribut marquesVehicules de type Set donnant l’ensemble des marques de véhicules gérés par GestionFlotte.```java
Set<String> marquesVehicules = new HashSet<>();
```
void syncMarquesVehicules() permettant de remplir cet ensemble à partir de la liste de tous les véhicules (listeGlobale).String getListeMarquesVehicules() qui renvoie une chaîne de caractères représentant les données contenues dans marquesVehicules (exemple: "Mercedes,Toyota,Peugeot")GestionFlotte un attribut modelesVehicule de type Set qui permet de gérer l’ensemble des marques et modèles de véhicule (un modèle de véhicule pouvant être présent plusieurs fois dans listeGlobale).syncModelesVehicules qui permet de remplir automatiquement modelesVehicule (lire la documentation de Set.addAll(Collection) et souvenez-vous de l’implémentation de la méthode equals)String getListeModelesVehicules() qui renvoie une chaîne de caractères représentant les données contenues dans modelesVehicule (exemple: "[Mercedes/Classe A],[Toyota/Prius],[Toyota/Yaris],[Peugeot/Partner]")Dans ce qui suit, vous traiterez les cas d’erreurs avec des exceptions (pour lesquelles vous créerez des classes).
Dans cette partie, nous allons références les véhicules par leur identifiant.
Map permettant de stocker la liste des véhicules en les référençant par leur identifiant.quel est le type de l’identifiant ?
getVehicule(int id)Map permettant de classer les véhicules par constructeur (marque).quel doit être le type de la clef ?
getListeVehiculePourMarque(...)Map avec celui des listes de véhicules (à chaque ajout et/ou via une méthode de synchronisation)quelle manière semble la plus appropriée ?
Utilisez un Iterator pour afficher l’ensemble des éléments de la liste des véhicules légers.