Hallo! In dieser Lektion sehen wir uns die Java HashMap genauer an. Bisher haben wir uns mit Datenstrukturen beschäftigt, in denen die Elemente selbst gespeichert werden. In einem Array oder einer ArrayList/LinkedList speichern wir eine bestimmte Anzahl von Elementen. Aber was ist, wenn sich unsere Aufgabe ein wenig ändert?
Stell dir folgende Aufgabe vor: Erstelle eine Liste mit 100 Personen, in welcher der Name und die Ausweisnummer jeder Person gespeichert sind. Im Prinzip ist das gar nicht so schwierig. Du kannst zum Beispiel beides in eine Zeichenkette packen und dann eine Liste mit diesen Zeichenketten erstellen:
"Amelia Aguilar, 4211 717171".
Aber diese Lösung hat zwei Nachteile. Erstens brauchen wir vielleicht die Möglichkeit, nach der Ausweisnummer zu suchen. Und das wird bei diesem Format der Informationsspeicherung problematisch sein.
Zweitens hält uns nichts davon ab, zwei verschiedene Personen mit der gleichen Ausweisnummer zu erstellen. Und das ist das größte Manko unserer Lösung. Das sollte niemals erlaubt sein: Keine zwei Personen haben die gleiche Ausweisnummer.
Eine neue Datenstruktur kommt uns zu Hilfe: Map. Sie wird auch als „assoziatives Array“ bezeichnet, aber dieser Begriff wird nur noch selten verwendet. Im Allgemeinen wird sie als „Dictionary“ oder „Map“ bezeichnet. :)
Wie unterscheidet sie sich grundlegend von den Datenstrukturen, die wir bisher betrachtet haben?
In erster Linie dadurch, dass die Daten in einer Map als Schlüssel-Wert-Paare gespeichert werden. Alles kann als Schlüssel und Wert dienen: Zahlen, Strings oder Objekte anderer Klassen.
Heute schauen wir uns die häufigste Implementierung der Map-Klasse an: Java HashMap.
Was müssen wir also über HashMap in Java wissen?
Sie ist sehr einfach zu erstellen:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
}
Hier erstellen wir ein Dictionary, das Elemente als „Zahl-String“-Paare speichert. Die Zahl dient als Schlüssel und der String als Wert. Außerdem geben wir den Schlüsseltyp (Integer) und den Wertetyp (String) an.
Warum?
Erstens: Ein HashMap-Schlüssel ist immer eindeutig. Das passt perfekt zu unserer Aufgabe, denn wir können die Ausweisnummer als Schlüssel verwenden und Duplikate vermeiden. Der Wert ist ein String mit dem vollständigen Namen (verschiedene Personen können denselben Namen haben; das ist nichts, worüber wir uns Gedanken machen müssen).
Das Hinzufügen eines neuen Paares zur HashMap sieht wie folgt aus:
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames);
}
}
Wir verwenden dafür die Methode put(). Außerdem überschreibt die HashMap die toString()-Methode, sodass sie auf der Konsole angezeigt werden kann. Die Ausgabe sieht dann so aus:
{212133=Bridget Logan, 8082771=Donald John Trump, 162348=Iwan der Große}
Überprüfen wir nun, ob die Schlüssel wirklich eindeutig sind.
Versuchen wir, ein neues Element mit einem Schlüssel hinzuzufügen, der bereits in der Map verwendet wurde:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
passportsAndNames.put(162348, "Albert Kent");// This key has already been used
System.out.println(passportsAndNames);
}
Ausgabe:
{212133=Bridget Logan, 8082771=Donald John Trump, 162348=Albert Kent}
Wie du siehst, wurde der vorherige Wert, der mit dem Schlüssel 162348 verbunden war, überschrieben.
Wir verwenden den Begriff „Schlüssel“ aus einem bestimmten Grund. Auf die Werte in einer HashMap wird über den Schlüssel zugegriffen, aber nicht andersherum. Der Schlüssel kann nicht über einen Wert ermittelt werden, da die Werte möglicherweise nicht eindeutig sind.
Das kann man deutlich sehen, wenn man ein Element aus der HashMap abruft oder entfernt:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
String lidiaName = passportsAndNames.get(212133);
System.out.println(lidiaName);
passportsAndNames.remove(162348);
System.out.println(passportsAndNames);
}
Um einen Wert abzurufen oder ein Paar aus dem Dictionary zu entfernen, müssen wir den eindeutigen Schlüssel, der dem Wert entspricht, an get() bzw. remove() übergeben. Anders als Arrays und Listen hat eine HashMap in Java keine numerischen Indizes: Auf die Werte wird über den Schlüssel zugegriffen.
Konsolenausgabe:
Bridget Logan
{212133=Bridget Logan, 8082771=Donald John Trump}
Mit den Klassen ArrayList und LinkedList können wir überprüfen, ob die Liste ein bestimmtes Element enthält.
Mit Java HashMap können wir das ebenfalls. Außerdem können wir dies für beide Mitglieder des Paares tun: Dafür gibt es die Methoden containsKey() (sucht nach einem Schlüssel) und containsValue() (sucht nach einem Wert).
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames.containsKey(11111));
System.out.println(passportsAndNames.containsValue("Donald John Trump"));
}
Ausgabe:
false
true
Eine weitere praktische Funktion der HashMap in Java ist die Tatsache, dass du separate Listen mit allen Schlüsseln und allen Werten abrufen kannst.
Dafür gibt es die Methoden keySet() und values():
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
Set keys = passportsAndNames.keySet();
System.out.println("Keys: " + keys);
ArrayList<String> values = new ArrayList<>(passportsAndNames.values());
System.out.println("Values: " + values);
}
}
Die Schlüssel werden in ein Set extrahiert, das wir noch nicht behandelt haben. Es ist insofern besonders, als es keine sich wiederholenden Elemente enthalten kann. Das Wichtigste ist nun, dass die Liste aller Schlüssel aus einer HashMap in eine eigene Collection geholt werden kann.
In unserem Beispiel haben wir die Werte in einer gewöhnlichen ArrayList gespeichert.
Konsolenausgabe:
Schlüssel: [212133, 8082771, 162348]
Werte: [Bridget Logan, Donald John Trump, Iwan der Große]
Die Methoden size() und clear() tun genau dasselbe wie in den zuvor besprochenen Strukturen: Die erste gibt die Anzahl der Elemente zurück, die sich derzeit im Dictionary befinden, die zweite löscht alle Elemente.
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames.size());
passportsAndNames.clear();
System.out.println(passportsAndNames);
}
Ausgabe:
3
{}
Um zu prüfen, ob mindestens ein Element in unserer HashMap vorhanden ist, können wir die Methode isEmpty() verwenden:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
if (!passportsAndNames.isEmpty()) {
System.out.println(passportsAndNames);
}
}
Ausgabe:
{212133=Bridget Logan, 8082771=Donald John Trump, 162348=Iwan der Große}
Jetzt geben wir nur noch nach einer Vorprüfung etwas auf der Konsole aus. :)
Ein weiterer interessanter Punkt ist, dass zwei Maps zu einer kombiniert werden können. Dies wird mit der Methode putAll() erreicht. Wir rufen sie auf der ersten HashMap auf, übergeben die zweite als Argument und die Elemente aus der zweiten werden der ersten hinzugefügt:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
HashMap<Integer, String> passportsAndNames2 = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
passportsAndNames2.put(917352, "Clifford Patrick");
passportsAndNames2.put(925648, "Mitchell Salgado");
passportsAndNames.putAll(passportsAndNames2);
System.out.println(passportsAndNames);
}
Ausgabe:
{917352=Clifford Patrick, 212133=Bridget Logan, 8082771=Donald John Trump, 925648=Mitchell Salgado, 162348=Iwan der Große}
Alle Paare in passportsAndNames2 sind in passportsAndNames kopiert worden.
Betrachte nun ein etwas komplizierteres Beispiel. Genauer gesagt, das Iterieren über eine HashMap in einer Schleife.
for (Map.Entry<Integer, String> entry: passportsAndNames.entrySet()) {
System.out.println(entry);
}
Die Klasse Map.Entry bezeichnet das Schlüssel-Werte-Paar innerhalb des Dictionary. Die Methode entrySet() gibt eine Liste aller Paare in unserer HashMap zurück. Da unsere Map aus diesen Map.Entry-Paaren besteht, iterieren wir über Paare und nicht über einzelne Schlüssel oder Werte.
Ausgabe:
212133=Bridget Logan
8082771=Donald John Trump
162348=Iwan der Große
Vergiss auch nicht, die offizielle Oracle-Dokumentation zu HashMap zu lesen.
GO TO FULL VERSION