TD de Programmation Orientée Objets
Interfaces - Collections
Corrigé
dernière modification par Philippe.Genoud@imag.fr.
Philippe Genoud, Xavier Girod
import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Représente un ensemble de numéros de téléphone, cet ensemble contient au moins * un numéro (pas de liste vide). * * Une ArrayList est utilisée pour stocker les éléments de la liste. * */ public class AL_ListeNumTel implements ListeNumTel { /** * La liste des numéros */ protected List listNum_; //------------------------------------------------ // Constructeur //------------------------------------------------ /** * Construit une liste contenant le numéro spécifié en paramètre * @param numTel le numéro à insérer dans la liste */ public AL_ListeNumTel(NumTel numTel) { listNum_ = new ArrayList(); listNum_.add(numTel); } //------------------------------------------------------ // implementation de l'interface ListeNumTel. //------------------------------------------------------- /** ajoute un numéro à une position donnée dans la liste, sans effet si le numéro est déjà * présent dans la liste. * @param int index la position d'insertion dans la liste * @param num le numéro à ajouter * @return |
Exercice 1
a) la classe M_Annuaire incomplète. Seules les méthodes afficher, ajouterEntree, numeros et personnes sont implémentées. Le main suggéré dans l'enoncé du TP a été ajouté.
import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * implementation partielle de l'interface Annuaire en utilsiant une HashMap. * * @author genoud */ public class M_Annuaire implements Annuaire { /** * la table associative qui gère les données de l'annuaire */ private Map data_; //------------------------------------------------------- // constructeurs //-------------------------------------------------------- /** Construit un annauire vide */ public M_Annuaire() { data_ = new HashMap(); } //------------------------------------------------------ // implémentation de l'interface Annuaire //------------------------------------------------------ /** * affiche l'intégralité de l'annuaire, sous la forme d'une personne par ligne * suivie de ses numéros de téléphone. * */ public void afficher() { for (Iterator it = personnes(); it.hasNext(); ) { Personne p = (Personne) it.next(); System.out.println(p + " : " + data_.get(p)); } } /** ajoute une nouvelle entrée dans l'annuaire. Si p n'existe pas: on crée une nouvelle * association (p,nums) et le booleen |
b) exécution
La trace d'exécution
produite : ------------------------------Pourquoi cela ne marche pas ? Pourquoi on ne retrouve pas le numéro de téléphone de Jean DUPONT ? parceque la fonction hashCode utilisée pour les personnes est celle héritée de Object et elle est cohérente avec la méthode equals héritée aussi de Object. La méthode equals est définie dans la classe Object de
la manière suivante : La méthode hashCode retourne quant à elle un entier qui correspond à l'adresse mémoire de l'objet. Ainsi, deux objets Personne qui ont les même attributs ne sont pas égaux et n'ont donc pas le même hashcode. Quand on recherche les numéros de téléphone de Jean DUPONT, on utilise un objet distinct de celui qui a été employé pour ranger ces numéros dans la Map. Son hashcode n'est pas le même que celui utilisé pour ranger les valeurs dans la table. Aussi quand on fait la recherche dans la table est-il normal de ne rien trouver pour l'entrée désignée par ce hashcode. |
c) Modification la classe Personne de manière à corriger les erreurs détectées précédemment.
Il faut redéfinir equals de manière à ce que deux personnes qui ont les mêmes attributs soient considérées égales, et la fonction de hashCode pour intégrer dans le calcul les attributs utilisés pour tester l'égalité (deux personne égales doivent avoir le même hashcode).. public boolean equals(Object o) { if (! (o instanceof Personne) ) return false; Personne p = (Personne) o; return nom_.equals(p.nom_) && prenom_.equals(p.prenom_) && civilite_ == p.civilite_; // ATTENTION : les STring sont des objets, bien penser à utiliser equals // et non pas == pour comparer des chaînes } public int hashCode() { String nomPlusPrenom = nom_ + prenom_; return nomPlusPrenom.hashCode() + civilite_; // allez regarder la définition de hashCode // dans la classe String } l'exécution de M_Anuaire donne alors bien le résultat escompté. ------------------------------ |
Exercice 2
Après avoir remplacé HashMap par TreeMap dans la classe M_Annuaire et recompilé on obtient l'erreur suivante à l'exécution lorsque l'on tente d'ajouter une entrée à l'annuaire: java.lang.ClassCastException l'erreur provient du fait que pour que le TreeMap fonctionne il faut qu'il existe une relation d'ordre sur les éléments utilisés comme clé et pour cela que la classe représentant ceux-ci implémente l'interface Comparable de java.lang. Ce qui n'est pas le cas de la classe Personne d'où l'erreur d'exécution. Pour remédier à ce problème il faut donc que personne implémente cette interface. on change la déclaration de la classe de la manière suivante : public class Personne implements Comparable {implements Comparable { et on ajoute une implémentation de la méthode compareTo //------------------------------------------ // implementation de l'interface Comparable //------------------------------------------ public int compareTo(Object o) { if (! (o instanceof Personne) ) throw new ClassCastException("impossible de comparer un " + o.getClass().getName() + " à une Personne"); Personne p = (Personne) o; String s = this.nom_ + this.prenom_ + this.civilite_ ; return s.compareTo(p.nom_ + p.prenom_ + p.civilite_); } Après ces modfications, l'exécution d'afficher donne bien un annuaire dans l'ordre alphabétique. ------------------------------ Mr Robert AARGHH : 140361 (P) Mr Jean DUPONT : 140361 (P) Mlle Sophie DURAND : 151171 (D) Mr Louis DUSCHMOL : 140361 (P) ------------------------------ |
Exercice 3:
implémentation complète de la classe Annuaire
import java.util.TreeMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * implementation complète de l'interface Annuaire en utilsiant une TreeMap. * * @author genoud */ public class M_Annuaire implements Annuaire { /** * la table associative qui gère les données de l'annuaire */ private Map data_; //------------------------------------------------------- // constructeurs //-------------------------------------------------------- /** Construit un annauire vide */ public M_Annuaire() { data_ = new TreeMap(); } //------------------------------------------------------ // implémentation de l'interface Annuaire //------------------------------------------------------ /** * affiche l'intégralité de l'annuaire, sous la forme d'une personne par ligne * suivie de ses numéros de téléphone. * */ public void afficher() { for (Iterator it = personnes(); it.hasNext(); ) { Personne p = (Personne) it.next(); System.out.println(p + " : " + data_.get(p)); } } /** ajoute une nouvelle entrée dans l'annuaire. Si p n'existe pas: on crée une nouvelle * association (p,nums) et le booleen |
un programme de test interactif sur le modèle de celui proposé pour les listes de numéros de téléphone
public class AnnuaireTest { /** * l'annuaire */ private static Annuaire annu; /** * affiche un menu proposant les différentes opérations possibles sur l'annuaire */ private static void affMenu() { System.out.println("-----------------------------------------------"); System.out.println("1 : créer un annuaire vide "); System.out.println("2 : ajouter une personne "); System.out.println("3 : ajouter un numéro à une personne en début de liste "); System.out.println("4 : ajouter un numéro à une personne en fin de liste "); System.out.println("5 : afficher tous les numéros d'une personne "); System.out.println("6 : afficher le premier numéro d'une personne "); System.out.println("7 : supprimer une personne "); System.out.println("8 : supprimer un numéro d'une personne "); System.out.println("9 : afficher tout le contenu de l'annuaire "); System.out.println("0 : quitter l'application "); System.out.print("\nVotre choix : "); } private static NumTel lireNumTel() { System.out.print("numero : "); int num = LectureClavier.lireEntier(); System.out.print("type (T : Fixe professionnel, D : Fixe domicile, P : Portable, F : Fax, ? inconnu)"); char type = LectureClavier.lireChar(); return new NumTel(num,type); } private static Personne lirePersonne(){ System.out.print("nom : "); String nom = LectureClavier.lireChaine(); System.out.print("prénom : "); String prenom = LectureClavier.lireChaine(); System.out.print("civilite " + Personne.MR + " : Mr " + Personne.MME + " : Mme " + Personne.MLLE + " : Mlle "); int civil = LectureClavier.lireEntier(); return new Personne(civil,nom,prenom); } private static void ajouterPersonne() { System.out.println("\nPersonne à ajouter"); Personne p = lirePersonne(); System.out.println("\nliste de ses numéros"); System.out.println("Premier numéro"); NumTel num = lireNumTel(); ListeNumTel l = new AL_ListeNumTel(num); System.out.println("autre numéro O/N ?"); boolean encore = LectureClavier.lireOuiNon(); while (encore) { num = lireNumTel(); l.ajouterFin(num); System.out.println("autre numéro O/N ?"); encore = LectureClavier.lireOuiNon(); } annu.ajouterEntree(p,l); } private static void ajouterNumeroDebut() { System.out.println("\nPersonne à laquelle le numéro doit être ajouté"); Personne p = lirePersonne(); System.out.println("numéro de téléphone à ajouter au début de la liste : "); NumTel num = lireNumTel(); annu.ajouterNumeroDebut(p,num); } private static void ajouterNumeroFin() { System.out.println("\nPersonne à laquelle le numéro doit être ajouté"); Personne p = lirePersonne(); System.out.println("numéro de téléphone à ajouter en fin de la liste : "); NumTel num = lireNumTel(); annu.ajouterNumeroDebut(p,num); } private static void numeros() { System.out.println("\nPersonne pour laquelle vous voulez les numéros"); Personne p = lirePersonne(); ListeNumTel l = annu.numeros(p); System.out.println("ses numéros sont : " + l); } private static void premierNumero() { System.out.println("\nPersonne pour laquelle vous voulez le 1er numéros"); Personne p = lirePersonne(); NumTel n = annu.premierNumero(p); System.out.println("son premier numéro est : " + n); } private static void supprimerNumero() { System.out.println("\nPersonne pour laquelle vous voulez supprimer un numéro"); Personne p = lirePersonne(); System.out.print("numéro à supprimer "); int n = LectureClavier.lireEntier(); annu.supprimer(p,n); } private static void supprimerPersonne() { System.out.println("\nPersonne que vous voulez retirer de l'annuaire"); Personne p = lirePersonne(); annu.supprimer(p); } public static void main(String[] args){ annu = new M_Annuaire(); boolean encore = true; do { affMenu(); int rep = LectureClavier.lireEntier(); switch (rep) { case 0: System.out.print("Voulez vous vraimment quitter l'application O/N "); encore = ! LectureClavier.lireOuiNon(); break; case 1: annu = new M_Annuaire(); break; case 2: ajouterPersonne(); break; case 3: ajouterNumeroDebut(); break; case 4: ajouterNumeroFin(); break; case 5: numeros(); break; case 6: premierNumero(); break; case 7: supprimerPersonne(); break; case 8: supprimerNumero(); break; case 9: annu.afficher(); break; default: System.out.println("Mauvais numéro de commande"); break; } // fin du switch } while (encore); } }// AnnuaireTest |
exercice 4 : rechercher les personnes dans l'annuaire dont le nom commence par une chaîne donnée.
Il faut rajouter à l'annuaire la méthode suivante :
/** donne l'ensemble de toutes les personnes de l'annuaire dont le nom * débute par une chaîne donnée. * @param s1 la chaine pour la recherche * @return l'ensemble des personnes de l'annuaire dont le nom débute * par s1 */ public Set entreesPourChaine(String s1) { // on construit une chaine s2 dont le dernier // caractère est le suivant (selon l'ordre // alphabétique) du dernier caractère de s1. char[] carac = s1.toCharArray(); carac[carac.length-1] += 1; String s2 = new String(carac); // on crée deux personnes dont le nom est // respectivement s1 et s2 Personne fromPers = new Personne(s1,""); Personne toPers = new Personne(s2,""); // on cherche la "sous-map" qui correspond // à toutes les personnes dont le nom est // compris entre le nom de fromP et le nom de // toP SortedMap s = ((TreeMap)data_).subMap(fromPers, toPers); // le résultat est l'ensemble des clés de cette // "sous-map" return s.keySet(); } |