Eine Hashtable ist vor allem eine Map.
Eine Map funktioniert so: Wenn ich einen Schlüssel kenne, z.B, den Namen eines Mitarbeiters, kann ich dafür Werte aus der Map bekommen, z.B., wo dieser Mitarbeiter wohnt oder in welcher Abteilung er tätig ist.
Bei dieser Form einer Map (eben Hashtable) werden die Schlüssel intern in einem Array abgelegt. Allerdings werden diese Schlüssel nicht chronologisch oder sonst irgendeiner bestimmten Reihenfolge in diesem Array aufbewahrt, sondern gestreut, daher der Name. Diese Streuung erfolgt natürlich auch nicht zufällig. Die Platzierung, wohin also ein Schlüssel im Array kommt, hängt vom Streuwert ab. Dieser Wert wird von hashCode() zurückgegeben und üblicherweise aufgrund der Inhalte des Objekts berechnet. (Dazu muss diese Methode überdefiniert werden.) Wenn ein Eintrag in der Map gesucht wird, wird einfach der Streuwert des übergebenen Schlüssels berechnet und eben an der Stelle die Werte herausgelesen.
Beispiel: Ein String "Zeichenkette" landet z.B. an der Position 122, weil eben "Zeichenkette" im String steht. Ein anderer String, vielleicht "Java", landet woanders, einfach weil intern der String andere Werte für die Zeichenkette "Java" speichern muss als für "Zeichenkette".
Die Verwaltung der Schlüssel und der Zuordnungen (mapping) nimmt dir die Hashtable selbst ab. Du sagst einfach nur "put", wenn du Werte in die Map schreiben willst, und "get", wenn du Werte auslesen willst.
Wenn du Objekte eigener Klassen in Hashtables (oder HashSets oder HashMaps) aufbewahren willst, solltest du die Methode hashCode() deiner Klasse überdefinieren. Das Ziel dabei ist, dass der Hashcode möglichst eindeutig ist, also die Streuung möglichst groß. Wenn ein Objekt höchstens 2^32 verschiedene Zustände annehmen kann (also so viele wie ein int annehmen kann, das ist ja auch der Wertebereich der Hashfunktion), kann ein Hashcode immer eindeutig sein. In anderen Fällen ist das Finden einer geeigneten Hashfunktion meist schwierig. Man kann sich aber z.B. dadurch behelfen, dass man die Hashcodes der Eigenschaften mit einbezieht und XOR-verknüpft.
Hashfunktionen müssen für jede Datenstruktur neu definiert werden. Ein Beispiel zum hashen von Zeichenketten findest du in java.lang.String.
Ark