Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 15 - Collections II

## 15.6 Die Collection des Typs Map

- 15.6 Die Collection des Typs Map
  - 15.6.1 Abstrakte Eigenschaften
  - 15.6.2 Implementierungen

### 15.6.1 Abstrakte Eigenschaften

Eine Collection des Typs `Map` realisiert einen assoziativen Speicher, der Schlüssel auf Werte abbildet. Sowohl Schlüssel als auch Werte sind Objekte eines beliebigen Typs. Je Schlüssel gibt es entweder keinen oder genau einen Eintrag in der Collection. Soll ein Schlüssel-Wert-Paar eingefügt werden, dessen Schlüssel bereits existiert, wird dieses nicht neu eingefügt. Es wird lediglich dem vorhandenen Schlüssel der neue Wert zugeordnet. Der Wert wird also praktisch ausgetauscht.

Das Interface `Map` ist nicht von `Collection` abgeleitet, sondern eigenständig. Es definiert folgende Methoden:

    int size()
    boolean isEmpty()
    boolean containsKey(Object key)
    boolean containsValue(Object value)
    Object get(Object key)
    Object put(Object key, Object value)
    Object remove(Object key)
    void putAll(Map t)
    void clear()
    public Set keySet()
    public Collection values()
    public Set entrySet()
    boolean equals(Object o)
    int hashCode()
| java.util.Map

Die Methoden `size`, `isEmpty`, `remove`, `clear`, `equals` und `hashCode` sind mit den gleichnamigen Methoden des `Collection`-Interfaces identisch und brauchen daher nicht noch einmal erklärt zu werden.

Mit Hilfe von `put` wird ein neues Schlüssel-Wert-Paar eingefügt bzw. dem bereits vorhandenen Schlüssel ein neuer Wert zugeordnet. Die Methode `putAll` macht das für alle Paare der als Argument übergebenen `Map`. Auch hier gilt die Regel, daß ein Schlüssel, der bereits vorhanden ist, nicht neu eingefügt wird. Lediglich sein zugeordneter Wert wird ausgetauscht. Mit der Methode `get` kann der Wert zu einem Schlüssel beschafft werden. Da der Rückgabewert vom Typ `Object` ist, muß er auf den erwarteten Wert gecastet werden.

|  | Falls der angegebene Schlüssel nicht in der `Map` enthalten ist, wird `null` zurückgegeben. Hier ist Vorsicht geboten, denn ein `null`-Wert wird auch dann zurückgegeben, wenn die `Map` das Einfügen von `null`-Werten erlaubt und ein solcher diesem Schlüssel explizit zugeordnet wurde. Um diese beiden Fälle zu unterscheiden, kann die Methode `containsKey` verwendet werden. Sie gibt genau dann `true` zurück, wenn der gewünschte Schlüssel in der `Map` enthalten ist. Andernfalls liefert sie `false`. Analog dazu kann mit `containsValue` festgestellt werden, ob ein bestimmter Wert mindestens einmal in der `Map` enthalten ist. |  |
Hinweis

Im Vergleich zum `Collection`-Interface fällt auf, daß eine `Map` keine Methode `iterator` besitzt. Statt dessen kann sie drei unterschiedliche Collections erzeugen, die dann natürlich ihrerseits dazu verwendet werden können, einen Iterator zu liefern. Diese Collections werden als Views, also als Sichten auf die Collection bezeichnet:

- Die Methode `keySet` liefert die Menge der Schlüssel. Da per Definition keine doppelten Schlüssel in einer `Map` auftauchen, ist diese Collection vom Typ `Set`.
- Die Methode `values` liefert die Menge der Werte der `Map`. Da Werte sehr wohl doppelt enthalten sein können, ist der Rückgabewert lediglich vom Typ `Collection`, wird also typischerweise eine Liste oder eine anonyme Klasse mit entsprechenden Eigenschaften sein.
- Die Methode `entrySet` liefert eine Menge von Schlüssel-Wert-Paaren. Jedes Element dieser Menge ist vom Typ `Map.Entry`, d.h. es implementiert das lokale Interface `Entry` des Interfaces `Map`. Dieses stellt u.a. die Methoden `getKey` und `getValue` zur Verfügung, um auf die beiden Komponenten des Paares zuzugreifen.

Neben den im Interface definierten Methoden sollte eine konkrete `Map`-Implementierung zwei Konstruktoren zur Verfügung stellen. Ein leerer Konstruktor dient dazu, eine leere `Map` zu erzeugen. Ein zweiter Konstruktor, der ein einzelnes `Map`-Argument erwartet, erzeugt eine neue `Map` mit denselben Schlüssel-Wert-Paaren wie die als Argument übergebene `Map`.

### 15.6.2 Implementierungen

Das JDK stellt mehrere Implementierungen des `Map`-Interfaces zur Verfügung:

- Mit `AbstractMap` steht eine abstrakte Basisklasse für eigene Ableitungen zur Verfügung.
- Die Klasse `HashMap` implementiert das Interface auf der Basis einer Hashtabelle. Dabei wird ein Speicher fester Größe angelegt, und die Schlüssel werden mit Hilfe der Hash-Funktion, die den Speicherort direkt aus dem Schlüssel berechnet, möglichst gleichmäßig auf die verfügbaren Speicherplätze abgebildet. Da die Anzahl der potentiellen Schlüssel meist wesentlich größer als die Anzahl der verfügbaren Speicherplätze ist, können beim Einfügen Kollisionen auftreten, die mit geeigneten Mitteln behandelt werden müssen (bei der `HashMap` werden alle kollidierenden Elemente in einer verketteten Liste gehalten).
- Die altbekannte Klasse `Hashtable` implementiert seit dem JDK 1.2 ebenfalls das `Map`-Interface. Ihre Arbeitsweise entspricht im Prinzip der von `HashMap`, allerdings mit dem Unterschied, daß ihre Methoden synchronisiert sind und daß es nicht erlaubt ist, `null`-Werte einzufügen (die `HashMap` läßt dies zu).

Weitere Details zur Klasse `Hashtable` finden sich in Abschnitt 14.4. Dort gibt es auch ein kleines Anwendungsbeispiel, das wir hier in einer für die Klasse `HashMap` angepaßten Form zeigen wollen:

    001 /* Listing1507.java */
    002
    003 import java.util.*;
    004
    005 public class Listing1507
    006 {
    007   public static void main(String[] args)
    008   {
    009     HashMap h = new HashMap();
    010
    011     //Pflege der Aliase
    012     h.put("Fritz","f.mueller@test.de");
    013     h.put("Franz","fk@b-blabla.com");
    014     h.put("Paula","user0125@mail.uofm.edu");
    015     h.put("Lissa","lb3@gateway.fhdto.northsurf.dk");
    016
    017     //Ausgabe
    018     Iterator it = h.entrySet().iterator();
    019     while (it.hasNext()) {
    020       Map.Entry entry = (Map.Entry)it.next();
    021       System.out.println(
    022         (String)entry.getKey() + " --> " +
    023         (String)entry.getValue()
    024       );
    025     }
    026   }
    027 }
| Listing1507.java

Listing 15.7: Anwendung der Klasse HashMap

Der wichtigste Unterschied liegt in der Ausgabe der Ergebnisse. Während bei der `Hashtable` eine `Enumeration` verwendet wurde, müssen wir hier durch Aufruf von `entrySet` zunächst eine Menge der Schlüssel-Wert-Paare beschaffen. Diese liefert dann einen Iterator, der für jedes Element ein Objekt des Typs `Map.Entry` zurückgibt. Dessen Methoden `getKey` und `getValue` liefern den Schlüssel bzw. Wert des jeweiligen Eintrags. Alternativ hätten wir auch mit `keySet` die Menge der Schlüssel durchlaufen und mit `get` auf den jeweils aktuellen Wert zugreifen können. Dazu müßten die Zeilen 018 bis 025 im vorigen Listing durch folgenden Code ersetzt werden:

    Iterator it = h.keySet().iterator();
    while (it.hasNext()) {
      String key = (String)it.next();
      System.out.println(
        key + " --> " + (String)h.get(key)
      );
    }

Die Ausgabe des Programms unterscheidet sich nur in der Sortierung von der von Listing 14.3:

    Franz --> fk@b-blabla.com
    Fritz --> f.mueller@test.de
    Paula --> user0125@mail.uofm.edu
    Lissa --> lb3@gateway.fhdto.northsurf.dk

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de