1
Chapitre 9
Les Collections
Collections : Objectifs
2
2
Apprendre à utiliser les classes de collection (structures de données)
fournies dans la bibliothèque Java
Choisir les collections appropriées pour résoudre les problèmes de
programmation
Définition : Interfaces
3
3
Généralisation
Interface racine contenant toutes
Collection spéciale les opérations communes entre
toutes les collections Mappages de <clés, valeurs>
qui ne contient pas
des doublons
Collection Map
Set List Queue SortedMap
Collection spéciale avec des Map spéciale dans
opérations pour FIFO et des files laquelle les clés
SortedSet d'attente de priorités sont ordonnées
Stocke une séquence d'éléments
qui sont indexés
Set spécial qui
conserve l'ordre des
éléments
Spécialisation
Définition : Interfaces et classes
4
4
Ceci est une partie des interfaces et des implémentations de quelques structures de
données.
«interface» «interface»
Collection Map
«interface»
«interface» «interface» «interface»
Set List Queue SortedMap
HashSet «interface» Vector ArrayList LinkedList TreeMap HashMap
SortedSet
Stack
Linked TreeSet
HashSet Implementations
Collections et Persistance des données
5
5
RAM
Collections
Persistance
Réponses à des
requêtes
Minimiser
▪ Complexité Spatiale
▪ Complexité Temporelle
Itérateurs : java.lang.Iterable
6
Iterator it = items.iterator();
for (Item item : items) { while(it.hasNext()) {
}
System.out.println(item); = Item item = it.next();
System.out.println(item);
}
Cette déclaration est connue : “for-each”
<<interface>>
◼ For each item in items Iterable
Ceci est possible si les items sont de type +iterator():Iterator
Iterable
◼ Il suffit de définir la méthode iterator()
Collection (et ses sous-interfaces) implémente Collection
Iterable
On peut implémenter Iterable pour une nouvelle
classe Set List
Héritage entre les interfaces
Itérateurs bidirectionnels : interface ListIterator
7
Objet implémentant l’interface ListIterator (dérivée de Iterator)
◼ En plus des méthodes de Iterator
◼ Méthodes duales
◼ previous et hasPrevious
◼ Méthodes d’addition d’un élément à la position courante (add) ou de
modification de l’élément courant (set)
ListIterator iter = c.ListIterator(l.size()); // position courante: fin de liste
While (iter.hasPrevious())
{ Object o = iter.previous();
System.out.println(o);
}
Collections : Lists et Sets
8
8
Listes ordonnées
ArrayList
◼ Stocke une liste d'éléments dans un tableau de taille dynamique
LinkedList
◼ Permet une insertion et une suppression rapides des éléments de la
liste
Une liste est une collection qui maintient l'ordre de l’insertion de ses éléments.
Collections : Lists et Sets
9
9
Sets non ordonnés
Pas de doublons
HashSet
◼ Utilise des tables de hachage pour accélérer la recherche, l'ajout et la
suppression d'éléments
◼ Sans relation d’ordre
TreeSet
◼ Utilise un arbre binaire de recherche pour accélérer la recherche,
l'ajout et la suppression d'éléments
◼ Avec relation d’ordre
Un ensemble est une collection non ordonnée (ne garde pas l’ordre
d’insertion) d'éléments uniques.
Interface Set
10
Un ensemble est une collection non ordonnée
◼ Il ne prend pas en charge les éléments doublons
La collection ne garde pas trace de l'ordre dans lequel les éléments ont été
ajoutés
◼ Par conséquent, il peut effectuer ses opérations plus efficacement qu'une
collection ordonnée
Les classes HashSet et TreeSet implémentent l’interface Set.
Interface Set
11
HashSet : stocke les données dans une table de hachage
TreeSet : stocke les données dans un arbre binaire de recherche
Les deux implémentations organisent l'ensemble éléments de sorte que la
recherche, l'ajout et la suppression d'éléments est efficace
Les implémentations de Set organisent les
éléments afin qu'ils puissent les localiser
rapidement
TreeSet
12
◼ Utilser TreeSet pour les classes définissant une relation d’ordre
avec les interfaces Comparable et/ou Comparator
◼ String et Integer, par exemple
◼ Les nœuds sont disposés en « arbre » de sorte que chaque
nœud « parent » a jusqu'à deux nœuds enfants.
◼ Le nœud à gauche a toujours une valeur « plus petite »
◼ Le nœud à droite a toujours une valeur « plus grande »
Set names = new TreeSet();
Utilisation de Set (1)
13
Set names; Utilise le type interface pour la déclaration
d’une référence
Names = new HashSet(); Utilise TreeSet si on veut visiter les éléments
dans un ordre trié.
names.add("Romeo" ); names.size() est 1
names.add("Fred"); names.size() est 2
names.add("Romeo" ); names.size() est 2 car on ne peut pas ajouter des
doublons
if(names.contains("Fred")); La méthode contains vérifie si un objet est
contenu dans le set. Dans ce cas la méthode
retourne true.
Utilisation de Set (2)
14
System.out.println(names); Affiche le set dans le format [Fred, Romeo]. Les
éléments sont affichés selon la relation d’ordre de
tri utilisée.
for(String name : names) Utilise cette boucle pour visiter tous les éléments du
{ set.
…….
}
names.remove("Romeo"); names.size() est 1
names.remove("Juliet"); Ce n’est pas une erreur de supprimer un élément
inexistant. L’appel de la méthode n’a pas d’effet.
15
15
Rappel
Table de Hachage
Table de Hachage : Rappel
16
16
Table de Hachage : Rappel
17
17
Une table de hachage peut-être implantée sous la forme d'un tableau de listes
chainées. On doit également fournir une fonction de hachage dont le rôle est de
déterminer dans quelle liste une valeur se trouve ou doit être insérée. Elle prend en
argument la valeur et renvoie l'indice de la case du tableau contenant la liste
appropriée.
La fonction de
hachage dans cet
exemple calculera
simplement le
modulo de la valeur
par la taille du
tableau.
Problème !!! (1)
18
18
public class DateNais { public class Personne {
private int j; private String nom;
private int m; private String prenom;
private int a; private DateNais dn;
public DateNais(int j,int m,int a) public Personne(String nom,String prenom,
{ int j,int m,int a)
this.j=j; {
this.m=m; this.nom=nom;
this.a=a; this.prenom=prenom;
} this.dn=new DateNais(j,m,a);
public String toString() }
{ public String toString()
return this.j+"/"+this.m+"/"+this.a; {
} return this.nom+" "+this.prenom+"
} "+this.dn ;
}
}
Problème !!! (2)
19
19
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Personne p1=new Personne("Ali","Salah",2,5,1999);
Personne p2=new Personne("Ali", "Salah",2,5,1999);
Set s=new HashSet();
System.out.println("avant ajout 1");
s.add(p1);
System.out.println("après ajout 1");
System.out.println("avant ajout 2");
s.add(p2);
System.out.println("après ajout 2"); HashSet avec Doublons
System.out.println("size= "+s.size());
Iterator it = s.iterator();
while(it.hasNext())
?!!
{
Personne p=(Personne)it.next();
System.out.println(p);
}
}
}
Problème … Solution … equals … ?!!
20
20
DateNais
public boolean equals(Object o)
{
if( o instanceof DateNais)
{
DateNais d=(DateNais)o;
return (this.j==d.j) && (this.m==d.m) && (this.a==d.a);
}
else
return false;
}
Personne
public boolean equals(Object o)
{
if( o instanceof Personne2)
{
Personne2 p=(Personne2)o;
return nom.equals(p.nom) && prenom.equals(p.prenom) && dn.equals(p.dn);
}
else
return false;
}
Problème … Solution … equals … ?!!
21
21
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Personne p1=new Personne("Ali","Salah",2,5,1999);
Personne p2=new Personne("Ali", "Salah",2,5,1999);
Set s=new HashSet();
System.out.println("avant ajout 1");
s.add(p1);
System.out.println("après ajout 1");
System.out.println("avant ajout 2");
s.add(p2);
System.out.println("après ajout 2"); HashSet, equals avec
System.out.println("size= "+s.size());
Iterator it = s.iterator(); Doublons !!
while(it.hasNext())
{
Personne p=(Personne) it.next();
?!!
System.out.println(p);
}
}
}
Problème … Solution … Comparable … ?!!
22
22
public class Personne implements Comparable {
public int compareTo(Object o) {
if(nom.equalsIgnoreCase(((Personne)o).nom) &&
prenom.equalsIgnoreCase(((Personne)o).prenom) &&
matricule==((Personne)o).matricule &&
dn.equals(((Personne)o).dn))
return 0;
else if(this.matricule<((Personne)o).matricule)
return -1;
else return 1;
}
}
Problème … Solution … Comparable … ?!!
23
23
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Personne p1=new Personne("Ali","Salah",2,5,1999);
Personne p2=new Personne("Ali", "Salah",2,5,1999);
Set s=new HashSet();
System.out.println("avant ajout 1");
s.add(p1);
System.out.println("après ajout 1");
System.out.println("avant ajout 2");
s.add(p2);
System.out.println("après ajout 2"); HashSet, compareTo avec
System.out.println("size= "+s.size());
Iterator it = s.iterator(); Doublons !!
while(it.hasNext())
{
Personne p=(Personne)it.next();
?!!
System.out.println(p);
}
}
}
Object : equals et hashCode
24
24
La classe Object possède deux méthodes qui sont relatives à l'identité
des objets : equals() et hashCode()
La méthode equals() permet de tester l'égalité de deux objets d'un point
de vue sémantique.
La méthode hashCode() permet de renvoyer la valeur de hachage de
l'objet sur lequel elle est invoquée.
Problème … Solution … hashCode … only
25
25
DateNais
public int hashCode() {
int hash = 1;
hash = 31 * hash + j;
hash = 31 * hash + m;
hash = 31 * hash + a;
return hash;
}
Personne
public int hashCode() {
int hash = 1;
hash = 31 * hash + nom.hashCode();
hash = 31 * hash + prenom.hashCode();
hash = 31 * hash + dn.hashCode();
return hash;
}
Problème … Solution … hashCode … only
26
26
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Personne p1=new Personne("Ali","Salah",2,5,1999);
Personne p2=new Personne("Ali", "Salah",2,5,1999);
Set s=new HashSet();
System.out.println("avant ajout 1");
s.add(p1);
System.out.println("après ajout 1");
System.out.println("avant ajout 2");
s.add(p2);
System.out.println("après ajout 2"); HashSet, hashcode avec
System.out.println("size= "+s.size());
Iterator it = s.iterator(); Doublons !!
while(it.hasNext())
{
Personne p=(Personne)it.next();
?!!
System.out.println(p);
}
}
}
Problème … Solution … hashCode … equals
27
27
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Personne p1=new Personne("Ali","Salah",2,5,1999);
Personne p2=new Personne("Ali", "Salah",2,5,1999);
Set s=new HashSet();
System.out.println("avant ajout 1");
s.add(p1);
System.out.println("après ajout 1");
System.out.println("avant ajout 2");
s.add(p2);
System.out.println("après ajout 2"); HashSet, hashcode, equals
System.out.println("size= "+s.size());
Iterator it = s.iterator(); SANS Doublons
while(it.hasNext())
{
Personne p=(Personne)it.next();
OK
System.out.println(p);
}
}
}
Object : equals et hashCode
28
28
Les spécifications imposent une règle à respecter lors de la redéfinition
de ces méthodes : si une classe redéfinit la méthode equals() alors elle
doit aussi redéfinir la méthode hashCode() et inversement. Le
comportement de ces deux méthodes doit être symétrique : si les
méthodes hashCode() et equals() sont redéfinies alors elles doivent
utiliser, de préférence, toutes les deux les mêmes champs car deux objets
qui sont égaux en utilisant la méthode equals() doivent obligatoirement
avoir tous les deux la même valeur de retour lors de l'invocation de leur
méthode hashCode(). L'inverse n'est pas forcément vrai.
Deux objets pouvant avoir le même hashcode, il faut alors utiliser la
méthode equals() pour déterminer s'ils sont identiques.
29
29
Rappel
Arbre binaire de recherche
Arbre binaire de recherche : Rappel
30
30
Un arbre binaire de recherche est une structure de données qui permet de
représenter un ensemble de valeurs si l’on dispose d’une relation d’ordre sur ces
valeurs. Les opérations caractéristiques sont : l’insertion, la suppression et la
recherche d’une valeur.
Ces opérations sont peu coûteuses si l’arbre n’est pas trop déséquilibré.
Arbre binaire de recherche
31
Les éléments stockés dans un arbre binaire de recherche doivent posséder
une relation d'ordre totale
Arbre binaire de recherche
32
Opérations réalisées sur les arbres binaires de recherche ont une complexité
temporelle logarithmique O(log n) dans le cas moyen.
➔ naviguer dans l'arbre avec une logique de recherche dichotomique
➔ plus performant que les listes
8 10 3 6 1 14 7 13 4
Arbre binaire de recherche
33
20 5 25 12 8 3 28 13 21 6
20
Arbre binaire de recherche
34
20 5 25 12 8 3 28 13 21 6
20
5
Arbre binaire de recherche
35
20 5 25 12 8 3 28 13 21 6
20
5 25
Arbre binaire de recherche
36
20 5 25 12 8 3 28 13 21 6
20
5 25
12
Arbre binaire de recherche
37
20 5 25 12 8 3 28 13 21 6
20
5 25
12
8
Arbre binaire de recherche
38
20 5 25 12 8 3 28 13 21 6
20
5 25
3 12
8
Arbre binaire de recherche
39
20 5 25 12 8 3 28 13 21 6
20
5 25
3 12 28
8
Arbre binaire de recherche
40
20 5 25 12 8 3 28 13 21 6
20
5 25
3 12 28
8 13
Arbre binaire de recherche
41
20 5 25 12 8 3 28 13 21 6
20
5 25
3 12 21 28
8 13
42
20 5 25 12 8 3 28 13 21 6
20
5 25
3 12 21 28
8 13
6
43
20 5 25 12 8 3 28 13 21 6
20
5 25
3 12 21 28
8 13
6
Arbre binaire équilibré
44
❑ Un arbre binaire est équilibré si la hauteur du sous-arbre droit de
n’importe quel nœud diffère de la hauteur du sous-arbre gauche d’au plus 1
❑ Un arbre binaire est parfaitement équilibré si le sous-arbre droit et le
sous-arbre gauche de chaque nœud possèdent la même hauteur
Arbre binaire équilibré
45
Arbre 1 Arbre 2
Arbre binaire équilibré
46
❑ Un arbre binaire est équilibré si sa hauteur est O(log2 n), où n est le
nombre de nœuds.
❑ Arbre AVL maintient la hauteur en O(log2 n) en veillant à ce que la
différence entre les hauteurs des sous-arbres gauche et droit soit au plus
de 1.
Arbre binaire équilibré
47
2, 12, 25, 34, 46, 71 et 81
(a)
(b)
(c)
48
48
Fin Rappel
Collections : Piles et Files
49
49
Une autre façon de gagner en efficacité dans une collection est de réduire le
nombre d'opérations disponibles
Exemples :
Pile
◼ Se souvient de l'ordre de ses éléments, mais ne vous permet pas
d'insérer des éléments dans toutes les positions
◼ LIFO (Last In First Out)
◼ Vous ne pouvez ajouter et supprimer que des éléments en haut
File
◼ Ajouter des éléments à une extrémité (la queue)
◼ Retirez-les de l'autre extrémité (la tête)
◼ Exemple: Une file de personnes attendant un caissier de banque
Map
50
50
Un Map (dictionnaire) stocke les clés, les valeurs et les associations entre elles
Exemple:
Clé (unique) barcode et livres
Clefs
Fournit un moyen simple de représenter un
objet (comme un code à barres numérique)
Valeurs
L'objet réel associé à la clé
Map
51
51
L’implémentation de la méthode hashCode pour la classe dont ses objets sont
sauvegardés dans l’une des collections HashMap, HashTable ou HashSet est
obligatoire.
Exemple Map
52
52
List, Set et Map : Comparaison
53
53
Interface Collection (1)
54
54
List, Queue et Set sont des interfaces héritant de l’interface
Collection
Toutes partagent les méthodes couramment utilisées suivantes
Méthodes de l’interface Collection
Collection coll = new ArrayList(); La classe ArrayList implémente
l’interface Collection
coll = new TreeSet(); La classe TreeSet implémente l’interface
Collection
int n = coll.size(); Retourne la taille de la collection. n est 0.
coll.add(" Harry"); Ajoute des éléments à la collection.
coll.add(" Sally");
String s = coll.toString(); Retourne String avec tous les éléments de
la collection. s est "[ Harry, Sally]"
System.out.println(coll); Invoque toString et affiche [Harry, Sally]
Interface Collection (2)
55
55
Méthodes de l’interface Collection
Collection coll = new ArrayList(); La classe ArrayList implémente
l’interface Collection
coll.remove("Harry" ); Supprime l’élément de la collection,
Boolean b = coll.remove(" Tom"); retourne false si l’élément n’est pas présent.
b est false.
b =coll.contains(" Sally" ); Vérifie si la collection contient l’élément
donné. b est true.
for(String s : coll) On peut utiliser " for each " avec n’importe
{ quelle collection. Cette boucle affiche les
System.out.println(s); éléments dans des lignes séparées.
}
Iterator iter = coll.iterator(); Un itérateur est utilisé pour visiter les
éléments dans la collection
LinkedList
56
56
Les listes chaînées utilisent des références pour maintenir une liste ordonnée
de « nœuds »
La « tête » de la liste fait référence au premier nœud
Chaque nœud a une valeur et une référence au nœud suivant
Ils peuvent être utilisés pour mettre en œuvre
◼ Interface List
◼ Interface Queue
LinkedList
57
57
LinkedList list = new LinkedList(); List vide
list.addLast("Harry" ); Ajoute un élément à la fin de la liste. Similaire
à add
list.addFirst("Sally" ); Ajoute un élément au début de la liste. list
contient [Sally, Harry]
list.getFirst(); Retourne l’élément sauvegardé au début de la
liste; dans ce cas "Sally"
list.getLast(); Retourne l’élément sauvegradé à la fin de la
liste; dans ce cas "Harry"
String removed = list.removeFirst(); Supprime le premier élément et le retourne.
Elément supprimé "Sally« et list est [Harry]. A
utiliser removeLast pour supprimer le dernier
élément.
ListIterator iter = list.listIterator(); Fournit un itérateur pour visiter tous les
éléments.
Itérateur List
58
58
❑ On traverse LinkedList avec ListIterator
▪ Garde une trace de votre position dans la liste.
LinkedList employeeNames = new LinkedList();
ListIterator iter = employeeNames.listIterator();
❑ Utiliser un itérateur pour :
▪ Accéder aux éléments dans linkedlist
▪ Visiter des noeuds autre que le premier et le dernier.
Utilisation des itérateurs
59
59
Considérez un itérateur comme curseur entre deux éléments
ListIterator iter = myList.listIterator()
iterator.next();
iterator.add(“J”);
❑ Notez que le type de ListIterator doit correspondre au type de
LinkedList
Sélection de la collection adéquate
60
60
Collections
61
61