TP
Généricité & collections en JAVA
Objectifs
‐ ‐
S’initier au concept de généricité. Apprendre à manipuler génériques
les collections
Temps alloué
Outils
4h30
Eclipse
La généricité
Exercice 1 : Pile générique en représentation chaînée
Vous disposez du code suivant représentant l’implémentation d’une pile
// interface Pile interface Pile {
public boolean estVide(); public Object dernier(); public void depiler(); public void empiler(Object o);
} /* Le recours à une interface peut s’expliquer par la possibilité après de faire le choix de la représentation physique voulue (chainée ou contigüe) */ // Représentation chainée : listes de Object class Noeud {
Object info; Noeud suivant;
} // implémenter les Pile avec des listes (représentation chainée) class PileListe implements Pile{ private Noeud sommet; public PileListe(){
sommet = null;
} public boolean estVide(){
return (sommet == null);
} public Object dernier(){
return sommet.info;
} public void empiler(Object o){
Noeud n = new Noeud(); n.info = o; n.suivant = sommet; sommet = n;
} public void depiler(){
sommet = sommet.suivant;
1
}
} class TestPile{
public static void main(String[]args){
PileListe p = new PileListe(); for(int i = 0 ; i < 10 ; i++)
p.empiler(new Integer(i));
while(!p.estVide()){ System.out.println((Integer) p.dernier()); p.depiler();
} // Tester aussi p.empiler("L'entier " + i); /* System.out.println((Integer) p.dernier());
=> Erreur à l'exécution de casting => System.out.println(p.dernier()); => Ouverture sur les exceptions */ /* Remplacer while(!p.estVide()) par for(int i = 0 ; i < 20 ; i++) => Erreur à l'exécution lors du dépilement d'une pile vide => Ouverture sur les exceptions mais surtout sur la généricité */
}
}
Donnez la version générique de l’implémentation de la pile
Les collections génériques
2
Les types génériques servent particulièrement dans les collections de données. Dans
l’API Java, la plupart de ces structures de données de bases sont déjà implémentées
dans le package java.util, qui fournit une petite hiérarchie.
Notez que toutes ces interfaces sont définies de manière générique : il faut donc
spécifier le type qu’elles utilisent lorsqu’on les implémente et qu’on les manipule.
‐
Publicité
L’interface de base est Collection qui désigne n’importe quel regroupement
d’objets.
o Un Set est une collection qui n’autorise pas la duplication d’un
élément.
o Une List est un ensemble d’éléments ordonnés, c’est à dire une
séquence d’éléments.
o Une Queue est une collection qui conserve un ordre de manipulation,
selon une logique FIFO. Elle est particulièrement utile pour traiter des
suites d’´évènements.
‐ Une Map n’est pas une collection : c’est un objet qui associe des clefs `à des
valeurs. Une table de hachage par exemple, sera vue comme une Map. D’autre
part les Set et les Map peuvent être Sorted, c’est `à dire que leurs ´éléments
seront constamment ordonnés et que cet ordre sera conservé même après
des opérations d’ajout ou de retrait d’´éléments.
3
Quelques implémentations de ces interfaces sont fournies, et dont le nom indique la
méthode utilisée pour leur implémentation: ArrayList, LinkedList, HashSet,
HashMap, ...
D’autre part la classe Collections (avec un ‘s’) regroupe un grand nombre de
fonctions statiques utiles aux manipulations de ces structures de données.
Enfin, il faut noter que l’interface Collection étend l’interface Iterable. Cette dernière
demande juste qu’un objet puisse obtenir un Iterator avec la méthode iterator(). Un
Iterator doit implémenter les méthodes hasNext() et next() (optionnellement
remove()). Le gros avantage d’un objet Iterable est qu’on peut le manipuler avec la
construction foreach :
List<Integer> l = new LinkedList<Integer>();
/* do something with l */
for (Integer i : l) {
/* do something with i */
}
Exercice 2 : Pile et collection générique (ArrayList)
Vous disposez maintenant du code suivant représentant l’implémentation contigu d’une pile.
public class PileTab {
int pos; int [] tab;
public PileTab(){
tab = new int[4]; this.pos = 0;
}
public void add(int e){
if (this.pos == tab.length){
System.out.println("Ajout impossible de "+e); //System.exit(1);
} else {
4
tab[this.pos] = e; this.pos ++;
}
}
public int remove(){
int elem = tab[this.pos1]; this.pos ; return elem;
Publicité
}
public boolean estVide(){
return this.pos == 0;
}
public int size(){
return this.pos;
}
public void affiche(){
for (int i = 0; i<this.pos; i++){
System.out.println(this.tab[i]);
}
}
public int getSommet(){
return this.tab[pos1];
}
}
Question :
1) Testez cette classe dans votre programme principal 2) Transformer ce code en un type paramétré PileGen de façon à ce qu’on puisse ajouter n’importe quel type d’objet dans la pile. Le champ tableau devient un champ ArrayList <T> dont la taille est extensible, donc vous n’avez plus besoin du champ pos, vous pouvez utiliser directement la méthode size() de l’ArrayList. Dans la méthode d’affichage, vous utiliserez la méthode classique de parcours.
3) Testez La classe PileGen dans votre programme principal en
Exercice 3 : Pile et collection générique (LinkedList)
Voici une interface définissant un type abstrait "Pile de <A>" avec les fonctionnalités classiques d'une pile :
public interface IPile <A> {
boolean estVide(); void empile(A a); A depile(); // retourne l'élément en sommet de pile et dépile int nbElements();
5
A sommet(); // retourne le sommet de pile mais ne le dépile pas
}
1) Écrivez une classe générique CPile qui implémente l'interface IPile. Vous stockerez les éléments de la pile dans une liste chaînée (instance de java.util.LinkedList, voir en annexe quelques méthodes publiques de cette classe).
2) Écrivez un petit programme qui crée et manipule des piles en instanciant la classe générique de différentes façons (par exemple pile de String, pile de Integer, …).
Exercice 4 : Compréhension de l’architecture des tâches
L’architecture des tâches est donnée à la figure 1 où le détail des classes
TacheElementaire et TacheComplexe n’est pas donné.
Une tâche est caractérisée par un nom et un coût. Une tâche est soit une tâche
élémentaire, soit une tâche complexe qui est alors composée de sous‐tâches.
Il est ainsi possible d’ajouter une sous‐tâche à une tâche complexe, ajouter(Tache)
ou de supprimer une sous‐tâche, supprimer(Tache). Le coût d’une tâche complexe
est la somme des coûts des tâches qui la composent.
1) Écrire en Java la classe TacheElementaire qui est une réalisation de
l’interface Tache.
6
public interface Tache {
/** Obtenir le nom de la tâche. */ String getNom();
/** Obtenir le coût de la tâche. */ int getCout();
}
Définition d’une tâche complexe
Nous nous intéressons maintenant à la classe TacheComplexe, en particulier à sa
relation avec l’interface Tache. Une tâche complexe est composée d’un nombre
quelconque de tâches. On décide d’utiliser l’interface java.util.Collection pour
Publicité
stocker les sous‐tâches. On l’utilisera bien entendu dans sa version générique.
Comme on souhaite pouvoir parcourir toutes les sous‐tâches d’une tâche complexe,
la classe TacheComplexe réalise l’interface java.lang.Iterable.
2) Indiquer quel est le principal intérêt de la généricité. 3) Écrire en Java la classe TacheComplexe. 4) Écrire un programme qui crée des tâches et des tâches complexes et qui
affiche leur coût total.
Exercice 5 : Collection générique HashMap
L’objectif de cet exercice est de réaliser un annuaire pour mémoriser des numéros
de téléphone et des adresses. Chaque entrée est représentée par une fiche à
plusieurs champs : un nom, un numéro et une adresse.
Travail à faire :
1) Écrire la classe Fiche. Celle‐ci devra avoir un constructeur à 3 paramètres
pour initialiser l’ensemble des attributs de la classe et 1 constructeur à 1
paramètre auquel on passera le nom (Les autres champs sont initialises par
défaut a ‐1 (numéro) et null (adresse)).
La classe Annuaire comporte une table associative (HashMap<String , Fiche>) qui
sera faite d’associations (un nom , une Fiche).
7
2) Écrire la classe Annuaire dont le constructeur permettra la création d’une
table associative de type HashMap. Celleci doit comporter les méthodes
suivantes :
a. public void getNBconttacts() qui retourne le nombre de contacts
dans l’annuaire
b. public void addContact(Fiche f)qui ajoute un contact à partir
d’une fiche passé en paramètre.
c. public void addContact(String s, int n, String a) qui ajoute
un contat a partir d’un nom, d’un numéro et d’une adresse passés en
paramètre.
d. public void addContact(String s) qui ajoute un contact à partir
du nom passé en parameter (numéro et adresse par défaut)
e. public int getnumero(String name)qui retourne le numéro de
telephone associé au nom passé en paramètre
f. public void affiche()qui affiche les contacts dans l’annuaire.
3) Réalisez une classe Test afin d’y tester toutes vos méthodes
Annexe
8
la classe LinkedList java.util Class LinkedList<E> java.lang.Object java.util.AbstractCollection<E> java.util.AbstractList<E> java.util.AbstractSequentialList<E> java.util.LinkedList<E>
Type Parameters : E ‐ the type of elements held in this collection Quelques méthodes publiques :
LinkedList() Constructs an empty list. void addFirst(E o) Inserts the given element at the beginning of this list. E element() Retrieves, but does not remove, the head (first element) of this
list. Throws : NoSuchElementException ‐ if this queue is empty.
E getFirst() Returns the first element in this list.
Throws:
NoSuchElementException ‐ if this list is empty.
9