1. « Partie2 »
Collections de type List
Collections de type Map
Tri des collections
Threads
Pr H.LAARAJ
hasLaaraj@gmail.com
http://lewebpedagogique.com/laarajbts 2015/2016
50
3. 9.1 définitions:
Une collection est un objet qui contient d'autres objets.
L'API de collections possède deux grande familles
chacune définies par une interface :
java.util.Collection : pour gérer un groupe d'objets par
l’index.
java.util.Map : pour gérer des éléments de type de paires
clé/valeur
Remarque:
Les classes et interfaces se trouvent dans le paquetage : java.util
52
4. 9.3 les interfaces utilisées dans les collections
L'API de collections propose un ensemble d'interfaces dont le but est de stocker de
multiples objets. Elle propose quatre 3 types de collections
List : collection d'éléments ordonnés qui accepte les doublons
Set : collection d'éléments non ordonnés par défaut qui n'accepte pas les doublons
Map : collection sous la forme d'une association de paires clé/valeur
53
un petit diagramme de classes sur la figure suivante schématisant cette hiérarchie.
ArrayList
Vector
LinkedList
HashMap
5. 9.4 collection de type List
(avec doublons)
L'API propose quelques classes concrètes pour les listes (java.util.List).
java.util.ArrayList : utilise un tableau en interne pour ranger les
données. Un ArrayList fournit un accès aux éléments par leur indice
très performant et est optimisé pour des opérations d'ajout/suppression
d'éléments en fin de liste.
java.util.LinkedList : utilise une liste chainée pour ranger les données.
L'ajout et la suppression d'éléments est aussi rapide quelle que soit la
position, mais l'accès aux valeurs par leur indice est très lente.
java.util.Vector : est une classe héritée de Java 1. Elle n'est conservée
dans l'API actuelle que pour des raisons de compatibilité ascendante
et elle ne devrait pas être utilisée dans les nouveaux programmes.
Dans tous les cas, il est préférable d'utiliser un ArrayList.
54
6. Quelques méthodes d’une collection de type List
Quelques méthodes disponibles dans l’interface Collection:
• int size() : retourne le nombre d'éléments portés par cette collection
• boolean isEmpty(): un booléen qui permet de tester si cette collection est vide ou pas.
• boolean contains(T t) : retourne true si l'objet passé en paramètre est contenu dans
cette collection.
• boolean add(T t) et boolean remove(T t) : permet d'ajouter (resp. de retirer) un objet à
cette collection.
• void clear() : efface la collection courante.
• Object[] toArray() : convertit la collection courante en tableau.
Voyons les méthodes que l’interface List ajoute à Collection:
• void add(int index, T t) : permettent d'insérer un élément à la position notée par index.
• T set(int index, T t) : permet de remplacer l'élément placé à la position index par celui
passé en paramètre. L'élément qui existait est retiré de la liste, et retourné par cette
méthode.
• T get(int index) : retourne l'élément placé à l'index passé en paramètre.
• T remove(int index) : retire l'élément placé à l'index passé en paramètre. Cet élément
est retourné par la méthode.
• int indexOf(Object o) : retournent respectivement le premier index de l'objet passé en
paramètre dans cette liste.
55
7. Exemple 9.4.1:
Créer un objet col de type List
Ajouter 3 objets de type Personne à la collection col
Afficher les éléments de la collection col
Exemple 9.4.2:
Expliquer ce qu’elle va faire l’instruction suivante
System.out.println(col.remove(col.indexOf(p)));
56
Exemples:
8. Solution de l’exemple 9.4.1
List<Personne> col = new ArrayList<Personne>();
//-------------ou bien: List<Personne> col = new Vector<Personne>();
Personne p1 = new Personne ("a","adil",15);
Personne p2= new Personne ("b","hoda",25);
Personne p3 = new Personne ("c","farid",22);
col.add(p1);
col.add(p2);
col.add(1,p3);
for(Personne p : col)
p.afficher();
/*-----------Ou bien
for(int i=0;i<col.size();i++)
col.get(i).afficher();
*/
57
Resultat de l’execution:
a adil 15
c farid 22
b hoda 25
9. Solution de l’exemple 9.4.2
Explication de l’instruction :
System.out.println(col.remove(col.indexOf(p)));
col.indexOf(p) retourne l’index de l’objet p
col.remove(col.indexOf(p)) supprime et retourne l’objet stocké
dans l’index retourné par la méthode col.indexOf(p)
Alors :
System.out.println(col.remove(col.indexOf(p))); afficher l’objet
qu’est supprimé et retourné par la méthode:
col.remove(col.indexOf(p))
58
10. 9.5 tri des collections de type List
1ere méthode L'interface Comparable
Tous les objets qui doivent définir un ordre naturel utilisé par le tri d'une
collection doivent implémenter cette interface.
Cette interface ne définit qu'une seule méthode :
int compareTo(Object) qui doit renvoyer :
une valeur <0 si l'objet courant est inférieur à l'objet fourni
une valeur >0 si l'objet courant est supérieur à l'objet fourni
une valeur = 0 si l'objet courant est égal à l'objet fourni
A l’aide de cette méthode de comparaison, nous pouvons trier une
liste d’objets grâce à la méthode Collections.sort(List l)
Remarque : String et Date implémentent déjà l’interface Comparable.
59
11. 2eme méthode L'interface Comparator
Cette interface représente un ordre de tri quelconque. Elle est utile pour
permettre le tri d'objets qui n'implémentent pas l'interface Comparable
ou pour définir un ordre de tri différent de celui défini avec Comparable.
Cette interface ne définit qu'une seule méthode : int compare(Object,
Object). qui compare les deux objets fournis en paramètre et renvoie :
une valeur < 0 si le premier objet est inférieur au second
une valeur > 0 si le premier objet est supérieur au second
une valeur = 0 si les deux objets sont égaux
A l’aide de cette méthode de comparaison, nous pouvons trier une liste
d’objets selon l'ordre précisé par l'objet Comparator grâce à la méthode
Collections.sort(List l, Comparator o)
60
12. Exemple 9.5:
Trier la collection col de l’exemple9.4.1 par l’ordre
croissance de l'âge en utilisant :
1. Interface Comparable<T>
2. Interface Compartor<T>
61
13. Solution 9.5 (Comparable)
//classe Main
List<Personne> col = new ArrayList<Personne>();
col.add(new Personne ("a","adil",35));
col.add(new Personne ("b","hoda",25));
col.add(new Personne ("c","farid",2));
Collections.sort(col);
//sort(col) trier les objets de col par le critère de tri
//défini dans la méthode int compareTo(Personne)
for(Personne p : col)
p.afficher();
62
public class Personne implements
Comparable<Personne> {
String nom;
String prenom;
int age;
Personne(String nom,String prenom,int age)
{ //code déjà fait }
void afficher() {//code déjà fait }
//redefinition de compareTo(T)
public int compareTo(Personne p) {
return age-p.age;
}
}
Resultat de l’exécution:
c farid 2
b hoda 25
a adil 35
14. Solution 9.5 (Comparator – classe anonyme)
//on utilise la classe anonyme pour trier col
List<Personne> col = new ArrayList<Personne>();
col.add(new Personne ("a","adil",35));
col.add(new Personne ("b","hoda",25));
col.add(new Personne ("c","farid",2));
Collections.sort(col,new Comparator<Personne>(){
public int compare(Personne p1, Personne p2) {
return p1.age-p2.age;
} } );
/*sort(col,Comparator) permet de trier la collection
avec le critère défini dans la classe anonyme qui
implémente l’interface Comparator*/
for(Personne p : col)
p.afficher();
63
public class Personne {
/*Avec cette 2eme méthode on laisse la classe
Personne tel qu’il est défini au départ */
String nom;
String prenom;
int age;
Personne(String nom,String prenom,int age)
{
//code déjà fait
}
void afficher() {
//code déjà fait
}
}
Resultat de l’exécution:
c farid 2
b hoda 25
a adil 35
15. Solution 9.5 (Comparator – lambda)
//on utilise l’expression lambda pour trier col
//Remarque : l’expression lambda est sortie en 2014
List<Personne> col = new ArrayList<Personne>();
col.add(new Personne ("a","adil",35));
col.add(new Personne ("b","hoda",25));
col.add(new Personne ("c","farid",2));
Collections.sort(col,(Personne p1, Personne p2)
-> p1.age-p2.age );
/*sort(col,Comparator) permet de trier la collection
avec le critère défini dans l’expression lambda:
Entrée de compare() -> sortie de compare() */
for(Personne p : col)
p.afficher();
64
public class Personne {
/*Avec cette 2eme méthode on laisse la classe
Personne tel qu’il est défini au départ */
String nom;
String prenom;
int age;
Personne(String nom,String prenom,int age)
{
//code déjà fait
}
void afficher() {
//code déjà fait
}
}
Resultat de l’exécution:
c farid 2
b hoda 25
a adil 35
16. 9.6 collection de type MAP<K,V>
L'interface Map définie les méthodes d'accès
V put(K clé, V valeur) associe une clé à une valeur, renvoie
l'ancienne valeur ou null
V get(K clé) demande la valeur pour une clé, retourne null si
il n'y a pas d'association
boolean remove(K clé) supprime le couple clé/valeur à partir
d'une clé, retourne vrai si l'association a été supprimée
Exemple:
Collection HashMap qui permet de stocker dans un tableau
une association de clé/valeur. Les clés ne peuvent pas être null
mais par contre, les valeurs « null » sont autorisées pour une
HashMap.
65
17. 9.7 parcourir une collection
Pour parcourir une collection, on utilise un objet
de type Iterator.
la classe java.util.Iterator<E> définie :
boolean hasNext() qui renvoie vrai s'il y a un
suivant
E next() qui renvoie l'élement courant et décale
sur l'élément suivant
void remove() qui retire un élement
précédemment envoyé par next()
66
18. Exemple 9.6 :
Créer un objet hm de type HashMap
Ajouter 3 objets de type Personne à la
collection hm
Afficher les éléments de la collection hm
en utilisant l’interface Iterator pour
parcourir hm
67
19. Solution de l’exemple 9.6
Map<String,Personne> hm = new HashMap<>();
Personne p1 = new Personne ("a","adil",35);
Personne p2= new Personne ("b","hoda",25);
Personne p3 = new Personne ("c","farid",2);
hm.put(p3.nom, p3);
hm.put(p1.nom, p1);
hm.put("null", null);
hm.put(p2.nom, p2);
Iterator<Personne> it=hm.values().iterator();
while (it.hasNext()) {
Personne p=it.next();
if(p!=null) p.afficher();
}
hm.get("a").afficher();// est equivalent à p1.afficher()
68
Resultat de l’execution:
a adil 35
b hoda 25
c farid 2
a adil 35
20. 9.7 Exercice
Soit la classe Dessin qui a comme attribut une collection de points
1. Ecrire la classe Dessin
2. Dans la méthode main():
Créer un dessin
Ajouter 4 points au dessin
Supprimer le première point en affichant un message de confirmation
Afficher les points de dessin trier par ordre croissante de X
69
Dessin
- collection : List<Point>
Dessin()
int getNbrePoints()
void ajouterPoint(Point p)
Point supprimerPoint(Point p)
void afficherPoints()
void trierPointsParX()
Point
- x : int
- y: int
Point(int x,int y)
String toString()
Int getX()
21. Solution de l’exercice 9.7
class Point {
private int x,y;
Point(int x, int y) { this.x=x; this.y=y; }
int getX() { return x; }
public String toString() {
return "Point: (" + x + ", " + y + ')';
}
public boolean equals(Object obj) {
final Point p = (Point) obj;
return x==p.x&&y==p.y;
}
}
70
22. Solution de l’exercice9.7(suite)
public class Dessin {
List<Point> collection ;
Dessin(){
collection =new Vector<>();
}
int getNbrePoints(){ return collection.size();}
void ajouterPoint(Point p){collection.add(p);}
Point supprimerPoint(Point p){
int index =collection.indexOf(p);
if(index>=0 && index<getNbrePoints())
return collection.remove(index);
else
return null;
}
71 void afficherPoints(){
System.out.println("Points de dessin sont :");
for(Point p:collection)
System.out.println(p);
}
void trierPointsParX(){
Collections.sort(collection,new
Comparator<Point>(){
public int compare(Point p1, Point p2) {
return p1.getX()-p2.getX();
}
});
}
}
23. Solution de l’exercice 9.7(suite)
public static void main(String[] args) {
Dessin d=new Dessin();
d.ajouterPoint(new Point(23,3));
d.ajouterPoint(new Point(4,7));
d.ajouterPoint(new Point(78,23));
d.ajouterPoint(new Point(6,77));
Point p=d.supprimerPoint(new Point(23,3));
if(p!=null)
System.out.println(p+" a été supprimé. ");
else
System.out.println("Erreur ; suppression d'un point . ");
d.trierPointsParX();
d.afficherPoints();
}
72
Résultat de l’exécution:
Point: (23, 3) a été supprimé.
Points de dessin sont :
Point: (4, 7)
Point: (6, 77)
Point: (78, 23)
25. 10.1 définition d’un Thread
Un thread est donc une portion de code capable de s'exécuter en parallèle à d'autres
traitements.
Les threads peuvent être créés comme instance d'une classe dérivée de la classe
Thread ou de l’interface Runnable.
Elles sont lancées par la méthode start(), qui demande à l'ordonanceur de thread de
lancer la méthode run() de la thread.
Cette méthode run() doit être implantée dans le programme.
Syntaxe:
74
class Monthread extends Thread {
…
public void run() {
// code qui s'exécute en parallèle
}
}
class Monthread implements Runnable{
…
public void run() {
// code qui s'exécute en parallèle
}
}
26. Exemple 10.1 :
1. Créer une classe Afficheur qui permet d’afficher à chaque seconde un
texte jusqu' à l’infini?
2. Instancier 2 objets de type Afficheur ?
3. Afficher les 2 objets à la fois (parallèlement)?
75
Afficheur
- texte : String
Afficheur(String t)
run() : void
27. Solution de l’exemple10.1
nous résoudrons cette exemple en utilisant 3 méthode:
1ère méthode en utilisant la classe Mère Thread
2ème méthode en implémentant l’interface Runnable et en
utilisant un enveloppe Thread à l’intérieur de la méthode
main() de la classe principale
3ème méthode en implémentant l’interface Runnable et en
utilisant un enveloppe Thread à l’intérieur de la classe
Afficheur
76
28. Solution 10.1 1ère méthode
//la classe principale
public static void main(String[] args) {
new Afficheur("DSI").start();
new Afficheur("SRI").start();
/* La methode start() de la classe mère Thread
lance la méthode run() */
}
77
public class Afficheur extends Thread {
private String texte;
public Afficheur(String texte){
this.texte=texte;
}
public void run(){
while(true){
System.out.println(texte);
try { Thread.sleep(1000);}
catch (InterruptedException ex) {
System.out.println (ex);
}}
}
}
Résultat de l’exécution:
DSI
SRI
SRI
DSI
DSI
SRI
SRI
DSI
…
29. Solution 10.1 2ème méthode
//la classe principale
public static void main(String[] args) {
new Thread(new Afficheur("DSI")).start();
new Thread(new Afficheur("SRI")).start();
/* L’enveloppe Thread(Runnable) lance la
méthode run() grâce à la méthode start() */
}
78
public class Afficheur implements Runnable {
private String texte;
public Afficheur(String texte){
this.texte=texte;
}
public void run(){
while(true){
System.out.println(texte);
try { Thread.sleep(1000);}
//sleep(int) est une méthode statique
catch (InterruptedException ex) {
System.out.println (ex);
}} }
}
Résultat de l’exécution:
DSI
SRI
SRI
DSI
DSI
SRI
SRI
DSI
…
30. Solution 10.1 3ème méthode
//la classe principale
public static void main(String[] args) {
new Afficheur("DSI");
new Afficheur("SRI");
//le constructeur lance les threads grâce à la
//méthode start()
}
79
public class Afficheur implements Runnable {
private String texte;
Thread t=new Thread(this);
public Afficheur(String texte){
this.texte=texte;
t.start();}
public void run(){
while(true){
System.out.println(texte);
try { Thread.sleep(1000);}
catch (InterruptedException ex) {
System.out.println (ex);
}}
}
}
Résultat de l’exécution:
DSI
SRI
SRI
DSI
DSI
SRI
SRI
DSI
…
31. 10.2 Cycle de vie d’un thread
80
Thread
prêt
Thread
crée
Thread
bloqué
Thread
en
exécution
new
start() stop()
sleep(t)
Wait()
notify()
yield()
run()
32. 10.3 synchronisation
Ce mécanisme du langage Java permet d'organiser les threads de manière à ce
que, pour certaines parties du programme, plusieurs threads ne soient pas en
même temps en train d'exécuter ces parties de programme.
Syntaxe :
1ère méthode 2ème méthode
81
class UneClasse {
private static final Object key = new Object();
…
void methode() {
synchronized(key) {
// bloc synchronisé
}
}
class UneClasse {
…
static synchronized methode() {
// code synchronisé
….
}
}
33. Exemple 10.3
Créer 2 threads qui remplissent à chaque 100ms un tableau statique
par des entiers .
Un thread rempli un nombre x, l’autre rempli un nombre y:
0 1 2 3 4 5 6 7 8 …
82
x y x y y x
x
y
x …
n : int
Static i : int
Static Tab[]: int
+Remplisseur(int n)
+remplir(int nb):void
+run():void
Remplisseur
34. Solution Exemple 10.3
ex.printStackTrace(); }
} // fin while
} // fin run()
}
//la classe principale
public static void main(String[] args) {
new Thread(new Remplisseur(8)).start();
new Thread(new Remplisseur(6)).start();
}
83
public class Remplisseur implements Runnable{
int n;
static int i , int tab[] = new int[20];
public Remplisseur(int n) { this.n=n; }
static synchronized void remplir(int nb){
tab[i]=nb;
System.out.println("-index: "+i+" -tab[i]: "+tab[i]);
i++; //être incrémenté seulement par un thread
}
public void run() {
while(i<tab.length) {
remplir(n); //méthode synchronisée
try { Thread.sleep(100); }
catch (InterruptedException ex) {
Résultat de l’exécution:
Rempir(n) est synchronisée****n’est pas synchronisée
-index: 0 -tab[i]: 8 -index: 0 -tab[i]: 6
-index: 1 -tab[i]: 6 -index: 0 -tab[i]: 6
-index: 2 -tab[i]: 8 -index: 2 -tab[i]: 8
-index: 3 -tab[i]: 6 -index: 3 -tab[i]: 6
-index: 4 -tab[i]: 8 -index: 4 -tab[i]: 6
-index: 5 -tab[i]: 6 -index: 4 -tab[i]: 6
… …
35. 10.4 Exercice:
On veut réaliser un code java qui permet de modéliser le schéma de client/serveur suivant:
84
Thread
Client
Thread
Client
Thread
Client
Liste
des
clients
en
service
Serveur
Thread
Service
Thread
Service
Thread
Service
Lancer dans la méthode main() les Threads suivants :
• Un Thread serveur qui envoi à chaque 5 seconde l’heure actuelle à tous les threads clients
• Trois Threads clients qui reçoivent la valeur de l’heure à chaque 5 seconde
Remarque: Pour l’envois des messages on n’utilise ni les flux ni les sockets car les objets s’exécutent
en même mémoire(RAM), mais on utilise les méthodes
36. Solution de l’exercice 10.4
85
c : Client
+Service(Client c)
+run():void
Service
static l : List<Client>
+Serveur()
+run():void
Serveur
servir
0..n
lancer
-nom : String
-message : String
estConnecter: boolean
estservir: boolean
+Client(String nom)
+connecter():void
+deconnecter():void
+run():void
Client
1
1
1
Le diagramme de classe qui nous aide à réaliser les classes est:
37. Solution de l’exercice 10.4
public void setMessage(String message) {
this.message = message;}
public void run() {
while(estConnecter){
if(message!=null)
System.out.println(message);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
System.out.println (ex);
}
} //fin de while
} //fin de run()
}
86
class Client implements Runnable {
private String nom;
private String message;
boolean estConnecter;
boolean estservir;
Client(String nom) {
this.nom=nom;
estConnecter=false;
estservir=false;}
public void connecter(){
Serveur.l.add(this); estConnecter=true; }
public void deconnecter(){
Serveur.l.remove(this); estConnecter=false; }
public String getNom() { return nom; }
public String getMessage() { return message; }
38. Solution de l’exercice 10.4(suite)
public class Service implements Runnable {
Client c;
Service(Client c){ this.c=c; }
public void run() {
while(true){
SimpleDateFormat d=new SimpleDateFormat("hh:mm:ss");;
String heure=d.format(new Date().getTime());
c.setMessage("Client "+c.getNom()+" : "+heure);
try { Thread.sleep(5000);}
catch (InterruptedException ex) {
ex.printStackTrace();
}
} }
}
87
39. Solution de l’exercice 10.4(suite)
public class Serveur implements Runnable {
static List<Client> l=new ArrayList<>();
public Serveur() { }
public void run() {
while(true){
for(int i=0;i<l.size();i++)
if(l.get(i).estConnecter==true && l.get(i).estservir==false)
{
new Thread(new Service(l.get(i))).start();
l.get(i).estservir=true;
System.out.println("le client "+ l.get(i).getNom() + " en cours de
traitement " );
}
}} }
88
40. Solution de l’exercice 10.4(suite)
public class Principale {
public static void main(String[] args) {
new Thread(new Serveur()).start();
Client c1=new Client("A");
Client c2=new Client("B");
Client c3=new Client("C");
c1.connecter();
c2.connecter();
c3.connecter();
new Thread(c1).start();
new Thread(c2).start();
new Thread(c3).start();
c3.deconnecter();
} }
89
Résultat de l’exécution:
le client A en cours de traitement
le client B en cours de traitement
Client B : 07:40:35
Client A : 07:40:35
Client B : 07:40:40
Client A : 07:40:40
Client B : 07:40:45
Client A : 07:40:45
Client B : 07:40:50
Client A : 07:40:50
…