Home

TD3 - Tables d'associations

1.  Liste d'associations

Nous allons effectuer des statistiques sur les prénoms des élèves du cours INF421. Pour cela, vous disposez du fichier ListeEleve.java qui contient une liste toutInf421 avec les noms et les prénoms des élèves du cours. Il suffit de copier le fichier dans votre répertoire de travail pour pouvoir accéder à ListeEleve.toutInf421 depuis votre programme.

1.1  Associations

Nous allons associer à chaque prénom le nombre d'élèves le possédant. Écrire pour cela une classe Assoc permettant de construire une liste d'associations prénom/nombre. En termes techniques, le prénom sert de clé et le nombre de valeur. Il s'agit donc de construire une liste dans laquelle chaque cellule contient une clé et une valeur.

Écrire les méthodes suivantes pour ajouter et consulter des associations dans une liste :

On pourra se contenter d'une implémentation très simple de liste d'associations dans laquelle plusieurs associations avec la même clé peuvent apparaître. Cependant, lors d'un get, c'est la dernière valeur associée à la clé qui doit être renvoyée.

1.2  Statistiques

Construire la liste d'associations correspondant au compte des prénoms dans la liste ListeEleve.toutInf421, et trouver ainsi les prénoms les plus répandus en INF421. Pour y voir plus clair, vous pouvez utiliser la commande sort qui permet de trier les lignes d'un fichier comme expliqué ci-dessous. Imaginons que l'exécution java Assoc affiche une sortie du type :

Julien 2
Albert 3
Moi 1
...

Vous pouvez rediriger la sortie de votre programme vers un fichier par la commande java Assoc > output.txt. (En unix, la syntaxe com > fic indique de rediriger la sortie de la commande com dans le fichier fic au lieu de l'afficher dans la fenêtre terminal.)

La commande sort --numeric-sort --key=2 output.txt permet alors d'afficher les lignes du fichier output.txt par deuxième mot croissant (dans l'ordre numérique). (Pour plus de détails sur la commande sort, exécuter man sort.)

On peut aussi obtenir directement la sortie triée par java Assoc | sort --numeric-sort --key=2. (En unix, la syntaxe com1 | com2 indique de rediriger la sortie de la commande com1 comme l'entrée de la commande com2.)

Une solution.

2.  La manière objet

Nous allons maintenant encapsuler une liste d'association dans une nouvelle classe TableAssoc :

class TableAssoc
{
    Assoc l ;

    TableAssoc () {
        l = null ;
    }
}

Écrire des méthodes non statiques pour ajouter une association dans une table (put), récupérer la valeur associée à une clé (get), tester s'il existe une association de clé donnée (containsKey), et afficher toutes les associations d'une table (print). (On utilisera au maximum le code déjà écrit à la question précédente.) Écrivez vos méthodes de sorte que les statistiques sur les prénoms puissent se faire par :

    public static void main (String[] args) {
        TableAssoc t = new TableAssoc () ;
        for (ListeEleve e = ListeEleve.toutInf421 ; e != null ; e = e.suivant) \{
            if (t.containsKey (e.prenom)) {
                int nb = t.get(e.prenom) ;
                t.put (e.prenom, nb+1) ;
            } else {
                t.put (e.prenom, 1) ;
            }
        }
         t.print () ;
    }

Une solution.

3.  Utilisation de la librairie java.util.Hashtable

Dans cette partie, nous allons utiliser l'implémentation de java des tables de hachage. Il y a donc beaucoup d'explications à lire et peu de code à taper...

3.1  Hashtable et Object

Une table de hachage est en effet une structure plus efficace pour retrouver les associations déjà insérées. Java fournit une structure de table de hachage par la classe java.util.Hashtable. Pour avoir accès à toutes les classes de java.util, votre programme doit commencer par la ligne :

import java.util.* ;

On peut ensuite obtenir une nouvelle table de hachage par :

    Hashtable t = new Hashtable () ;

(Sans le import java.util.* ;, il faudrait taper java.util.Hashtable t = new java.util.Hashtable () ;...)

L'utilisation de Hashtable est similaire à la classe TableAssoc que vous avez définit en question 2, sauf qu'elle est conçue pour pouvoir gérer n'importe quel type de clés et de valeurs. Elle contient ainsi des méthodes put, get et containsKey de fonctionalité similaire à celles que vous avez définit pour Table avec les signatures suivantes :

En java, toute les classes sont des cas particuliers de la classe Object. Ainsi on peut sans problème utiliser une String comme s'il s'agissait d'un Object, ce qui permet d'écrire t.containsKey ("John"), par exemple, sans que cela ne pose de problème. L'explication est très simple : tous les objets sont des « flèches », c'est-à-dire des adresses mémoire pour l'ordinateur. Rien n'empêche de considérer l'adresse d'une String comme étant l'adresse d'un Object (tant qu'on est sûr que cela ne peut pas provoquer d'erreur). Vous pouvez consulter la documentation de la classe Object et découvrir toutes les choses qui sont définies par défaut pour tout objet (y compris pour vos propres classes).

Par contre, les int ne sont pas des objets et le compilateur n'acceptera pas l'instruction t.put ("John", 9). Pour cela, java possède une classe Integer qui définit un objet contenant un int. On pourra ainsi effectuer t.put ("John", new Integer (9))

La transformation de Integer en Object ne pose pas non plus de problème. Par contre l'inverse n'est pas permis à priori. Ainsi il faudra écrire Object v = t.get ("John"), puis Integer vi = (Integer) v pour demander de transformer l'adresse v en une adresse vi vers un Integer. Si jamais l'objet pointé par v n'est pas un Integer, on obtiendra une erreur au moment de l'exécution de la conversion. On peut bien sûr écrire directement Integer vi = (Integer) t.get ("John"). Enfin, on peut obtenir la valeur int d'un Integer vi par vi.intValue ().

Enfin, pour afficher toutes les associations d'une Hashtable, on peut utiliser sa méthode String toString () qui renvoie une chaîne de caractère formée d'une suite de couples clé=valeur séparés par des virgules.

Reprenez la fonction main de la question 2 dans une classe UtilTable en utilisant java.util.Hashtable.

3.2  Enumeration

Pour obtenir la présentation voulue lors de l'affichage de la table (pour pouvoir utiliser sort), nous avons besoin de passer en revue toutes les associations de la table. Pour cela, la méthode Enumeration keys () renvoie un objet de type Enumeration. Enumeration est une interface qui donne les méthodes génériques pour parcourir tous les éléments d'une structure par un for de la manière suivante :

    for (Enumeration e = t.keys () ; e.hasMoreElements () ; ) {
        System.out.println (e.nextElement ()) ; 
    }

t.keys() renvoie une énumération fraîche des clés de la table t positionnée sur la première clé. e.hasMoreElements() renvoie vrai s'il y a au moins un élément à passer en revue. e.nextElement() renvoie l'élément courant (de type Object) et positionne l'énumération sur l'élément suivant.

Pour info, voici à quoi ressemble le code source de Enumeration :

interface Enumeration
{
    public boolean hasMoreElements() ;
    public Object nextElement() ; 
}

Écrire une méthode qui affiche les associations de la table de hachage sous la forme :

Julien 2
Albert 3
Moi 1
...

3.3  Test de rapidité

Pour vérifier l'efficacité des tables de hachage par rapport aux listes d'associations, tester les deux méthodes sur l'annuaire de l'École donné par la liste Annuaire.tout dans Annuaire.java. On utilisera TC.demarrerChrono() et TC.tempsChrono() pour mesurer le temps nécessaire à l'insertion de tous les prénom de la liste. Pour éviter de compter le temps de chargement de la liste, on effectuera des mesures selon le schéma suivant :

        ListeEleve charger = Annuaire.tout ;
        TC.demarrerChrono () ;

        // Insérer tous les prénoms.

        System.out.println ("Temps d'insertion = " + TC.tempsChrono () + "ms") ;

Pour étudier le comportement asymptotique, effectuer des tests en ajoutant les prénoms de la liste 1 fois, 10 fois, 100 fois, ... Expliquez les résultats que vous observez.

Une solution.

4.  Avant de partir

Déposez vos fichiers en utilisant la ligne de commande :

/users/profs/info/Depot/INF_421/deposer vos_fichiers_java TD_3 votre_groupe

vos_fichiers_java est la liste des fichiers que vous déposez (séparés par des espaces) et votre_groupe est votre numéro de groupe en informatique (entre 1 et 6).

5.  Supplément

Quelques exercices de plus.




INF421a 2004/05 - énoncé par Luc Maranget et Laurent Viennot, feuille de style par David Monniaux.
Last modified: Thu Sep 9 11:16:12 CEST 2004