La classe Hashtable permet de stocker des valeurs en les associant à des identificateurs uniques calculés à partir des clés.

Les clés et valeurs ne doivent pas possèder la valeur null, mais en revanche peuvent être des objets de n'importe quel type.

Les opérations d'ajout (put()), de récupération (get()) et de suppression (remove()) utilisent les méthodes equals() et hashCode().

  • La méthode hashCode() sert à calculer le code de hachage d'un quelconque objet Java.
  • La méthode equals() sert à vérifier l'égalité entre les codes de hachage de deux objets.

Lors du stockage d'une paire clé/valeur, l'objet représentant la clé est automatiquement soumise à la méthode hashCode() afin d'en récupérer un code de hachage identifiant d'une manière unique l'objet valeur. Ensuite, les codes de hachage sont comparés de telle sorte à vérifier si la clé est déjà présente dans la table de hachage. Si tel est le cas, la valeur donnée remplace l'ancienne.

//Extrait de la méthode put()
public synchronized Object put(Object key, Object value) {
  //...
  Entry tab[] = table;
  int hash = key.hashCode();
  int index = (hash & 0x7FFFFFFF) % tab.length;
  for (Entry e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
      Object old = e.value;
      e.value = value;
      return old;
    }
  }
  //...
}

Lors d'une recherche d'une valeur dans une table de hachaqe, un appel implicite à la méthode hashCode() est effectué afin d'en extraire le code de hachage de l'objet soumis à la méthode get(). Puis une comparaison automatique (equals()) est pratiquée entre le code de hachage de l'objet et ceux des clés de la table de hachage. Si les codes de hachage correspondent, alors la valeur associée est retournée.

// Méthode get de la classe Hashtable
public synchronized Object get(Object key) {
  Entry tab[] = table;
  int hash = key.hashCode();
  int index = (hash & 0x7FFFFFFF) % tab.length;
  for (Entry e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
      return e.value;
    }
  }
  return null;
}

De la même façon, la méthode containsKey(), vérifiant l'existence d'une clé au sein de la collection, utilise les méthodes hashCode() et equals(). Quant aux méthodes contains() et containsValue(), vérifiant l'existence d'une valeur au sein de la collection, emploient la méthode equals().

// Méthode get de la classe Hashtable
public synchronized boolean containsKey(Object key) {
  Entry tab[] = table;
  int hash = key.hashCode();
  int index = (hash & 0x7FFFFFFF) % tab.length;
  for (Entry e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
      return true;
    }
  }
  return false;
}

Une instance de classe Hashtable possède deux paramètres qui affecte ses performances. Il s'agît de la capacité initiale et le facteur de charge d'une table de hachage.

  • La capacité initiale représente le nombre de cases allouées initialement au moment de l'instanciation de la classe Hashtable.
  • Le facteur de charge représente la quantité par laquelle devra augmenter la capacité de l'objet Hashtable, lorsque le nombre d'entrées excède le produit du facteur de charge par la capacité courante de la table de hachage.

Par exemple, si une table de hachage possède une capacité initiale égale à 100 et un facteur de charge de 0.75, lorsque la table de hachage atteindra un taux de remplissage de 75 pourcents, alors la capacité est augmentée par un appel automatique à la méthode rehash().

public class CollectionMap {
  public static void main(String[] args) {
    TableHachage table = new TableHachage(100, 0.70f);
    if(table.isEmpty()){
      for(int i = 0; i < 100; i++) {
        table.put(new Integer(i), new Double(Math.pow(i, 2)));
      }
    }
  }
}

// Extension de la classe Hashtable...
import java.util.Hashtable;
import java.util.Map;

public class TableHachage extends Hashtable {
  public TableHachage(int capaciteInitiale, float facteurCharge) {
    super(capaciteInitiale, facteurCharge);
  }
  protected void rehash(){
    System.out.println("La méthode rehash() a été appelée !\n" 
                     + "La taille était alors égale à " + super.size());
    super.rehash();
  }
}

Par défaut, la capacité initiale et le facteur de charge d'un objet Hashtable sont respectivement 11 et 0.75. Sinon, deux constructeurs permettent de fournir soit la capacité initiale soit cette dernière et le facteur de charge durant l'instanciation de la classe Hashtable.

// par défaut
Hashtable table = new Hashtable();
// capacité initiale
Hashtable table = new Hashtable(100);
// capacité initiale et facteur de charge
Hashtable table = new Hashtable(100, 0.70f);

Un autre constructeur permet d'initialiser l'objet Hashtable avec les entrées d'une implémentation de la classe Map, telle que les collections AbstractMap, HashMap, Hashtable, IdentityHashMap, TreeMap et WeakHashMap.

HashMap collection = new HashMap(50, 0.75f);
for(int i = 0; i < collection.size(); i++) 
    collection.put(new Integer(i), new Integer(Math.pow(i, 2)));

Hashtable table = new Hashtable(collection);

La méthode putAll() propose cette fonctionnalité d'ajout groupé après que l'objet ait été créé.

HashMap collection = new HashMap(50, 0.75f);
for(int i = 0; i < collection.size(); i++) 
    collection.put(new Integer(i), new Integer(Math.pow(i, 2)));

Hashtable table = new Hashtable(collection);
table.putAll(collection);

La classe Hashtable dispose de plusieurs méthodes retournant des énumérations, ensembles et collections. Les clés et les valeurs peuvent être récupérées distinctement dans des objets Enumeration par les méthodes respectives keys() et elements(). D'autres méthodes extrayent les valeurs dans un objet Collection (values()) et les clés dans un objet Set (keySet()). Enfin une dernière méthode (entrySet()) renvoie une collection Set contenant les entrées de la table de hachage, sous la forme d'objets Map.Entry.

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class CollectionMap {
  public static void main(String[] args) {
    Hashtable table = new Hashtable();
    for(int i = 0; i < 26; i++){
      char car = (char)('a' + i);
      table.put(new Integer(i), new Character(car));
    }
    System.out.println("Taille de la table de hachage : " + table.size());
    Set ensemble = table.entrySet();
    Iterator i = ensemble.iterator();
    while(i.hasNext()){
      Object o = i.next();
      System.out.println(o.getClass().getName() + "\t" + o.toString());
      Map.Entry entree = (Map.Entry)o;
      System.out.println("\t" + entree.getKey() + " -> " + entree.getValue());
    }
  }
}
Exemple [voir]
import java.util.Enumeration;
import java.util.Hashtable;

public class CollectionMap {
  public static void main(String[] args) {
    String[] joursSemaine = {
                              "lundi", 
                              "mardi", 
                              "mercredi", 
                              "jeudi", 
                              "vendredi", 
                              "samedi", 
                              "dimanche"
                            };
    String[] daysOfWeek = {
                            "sunday", 
                            "monday", 
                            "tuesday", 
                            "wednesday", 
                            "thursday", 
                            "friday", 
                            "saturday"
                          };
    Hashtable tableHachage = new Hashtable();
    for(int i = 0; i < joursSemaine.length; i++){
      tableHachage.put(daysOfWeek[i < joursSemaine.length - 1 ? i+1 : 0], 
                       joursSemaine[i]);
      System.out.println(tableHachage.toString());
    }
    System.out.println("Taille de la table de hachage : " 
                                                    + tableHachage.size());
    int i = 1;
    Enumeration valeurs = tableHachage.elements();
    Enumeration cles = tableHachage.keys();
    while(valeurs.hasMoreElements() && cles.hasMoreElements()){
      System.out.println(i++ + " entrée : " + valeurs.nextElement() 
                                   + " -> " + tableHachage.remove(cles.nextElement()));
    }
    System.out.println("Taille de la table de hachage : " 
                                                    + tableHachage.size());
  }
}