Was sind Hashtable und Hashmap?
Die Hashtable
wurde ab Java 1.2 durch die Hashmap
ergänzt, um dem Collections
-Framework zu
entsprechen. Beides sind Implementierungen einer Hashtabelle,
die sich sehr ähnlich sind. Oracle empfiehlt die Verwendung
von Hashmap
, die jedoch im Gegensatz zur Hashtable
nicht synchronisiert ist.
Beide speichern
Schlüssel-Wert-Paare, deren Schlüssel immer eindeutig
sind. Beim Setzen eines neuen Wertes mit einem vorhandenen
Schlüssel wird dessen Wert überschrieben.
Schlüssel und Wert können prinzipiell Objekte eines
beliebigen Typs sein, die allerdings die Methoden equals()
und hashCode()
implementieren müssen. Hashmap
erlaubt, Hashtable
verbietet dagegen null
-Einträge.
Primitive Datentypen sind nicht erlaubt und werden beim Setzen
als Schlüssel oder Wert durch Boxing in Objekte
der entsprecheden Wrapper-Klassen gewandelt.
Konstruktoren von Hashtable
und Hashmap
besitzen zwei Parameter, die für die Effizienz und
Geschwindigkeit ihres Arbeitens entscheidend sind: die initial
capacity (anfängliche Kapazität) und ihr load
factor (Füllfaktor). Werden beide Werte im
Konstruktor nicht gesetzt, beträgt die
Anfangskapazität bei der Hashtable
11, bei der
Hashmap
16 Wertepaare, bei einem Füllfaktor
von jeweils 75% (0.75). Der Füllfaktor gibt an, in welchem
Maße die Sammlung maximal gefüllt sein darf. Wird
dieser Wert beim Einfügen neuer Elemente
überschritten, wird sie intern neu angelegt und berechnet.
Der benötigte Overhead der Sammlung hat mit der internen
Umsetzung der Einträge in ein Array und der Behandlung von
Kollisionen doppelter Hashwerte1 zu
tun und ist entscheidend bei geschwindigkeitskritischen
Anwendungen.
Für eine wenig zeitkritische Verwendung ist
es empfehlenswert, den Füllfaktor nicht zu ändern. So
ist der effizienteste Kompromiss zwischen Geschwindigkeit und
Speicherbedarf gewährleistet.
public class HashMapBeispiel { public static void main(String[] args) { HashMap<Integer, String> ht = new HashMap<Integer, String>(); String s; ht.put(2, "Gilera"); ht.put(1, "Moto Guzzi"); ht.put(3, "Morbidelli"); System.out.println("Durchlauf 1:"); for (Integer elem : ht.keySet()) { s = ht.get(elem); System.out.println(elem + " - " + s); } // Wert unter Key "1" wird ersetzt ht.put(1, "Ducati"); System.out.println("\nDurchlauf 2:"); Set<Integer> keys = ht.keySet(); Iterator<Integer> iterator = keys.iterator(); while (iterator.hasNext()) { Integer i = (Integer) iterator.next(); s = ht.get(i); System.out.println(i + " - " + s); } System.out.println("\nMoto Guzzi vorhanden? " + ht.containsValue("Moto Guzzi")); ht.put(4, "Moto Morini"); System.out.println("\nAnzahl Elemente nach einf\u00FCgen von '4': " + ht.size()); System.out.println("\nDurchlauf 3:"); Collection<String> vals = ht.values(); Iterator<String> iter = vals.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } ht.remove(4); System.out.println("\nAnzahl Elemente nach entfernen von '4': " + ht.size()); ht.clear(); System.out.println("\nAnzahl Elemente nach clear(): " + ht.size()); } }
Im Beispiel sind einige gängige Anwendungsweisen einer HashMap
demonstriert.
Die HashMap
ist ein generischer
Typ, bei dem festgelegt werden muss, welche Typen für die
Angabe von Schlüssel und Wert verwendet werden. Im Beispiel
sind dies Integer
für den Schlüssel und String
für den Wert. Die Angabe von primitiven int-Werten beim
Einfügen mit put()
ist möglich, da diese,
wie oben erläutert, in Integer
gewandelt
werden.
Beim Abfragen aller Einträge kann die Methode keySet()
verwendet werden. Sie liefert ein Set
aller
Schlüssel, das in einer Schleife durchlaufen werden kann.
Durch Übergabe der einzelnen Schlüssel als Parameter
an die Methode get()
kann dann auf seinen
zugehörigen Wert zugegriffen werden.
Nach dem Ersetzen des Wertes unter Schlüssel '1' wird neben
der oben verwendeten erweiterten
for-Schleife eine zweite Möglichkeit demonstriert,
um das Schlüsselset zu durchlaufen. Ein Iterator-Objekt
wird durch die Methode iterator()
erzeugt. Dessen
Methode hasNext()
prüft, ob weitere Elemente
vorhanden sind und kann so als Abbruchbedingung in einer
Schleife Verwendung finden. Dort wird das nächste
vorhandenen Element - hier der nächste Schlüssel - mit
next()
ermittelt.
Auch in der dritten und letzen
Schleife wird ein Iterator
verwendet, der nun
jedoch die mit values()
ermittelten Werte
durchläuft.
Duch Übergabe des Schlüssels an die Methode remove()
kann ein Eintrag aus der HashMap
entfernt werden;
durch clear()
kann diese vollständig geleert
werden.
1) Doppelte Hashwerte können intern bei der Berechnung aus den Schlüsseln der Einträge entstehen. Sie werden bei Auftreten durch spezielle Kollisionsmechanismen behandelt.
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.