Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien zum Buch
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichen und Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Schnittstellen, Aufzählungen, versiegelte Klassen, Records
9 Ausnahmen müssen sein
10 Geschachtelte Typen
11 Besondere Typen der Java SE
12 Generics<T>
13 Lambda-Ausdrücke und funktionale Programmierung
14 Architektur, Design und angewandte Objektorientierung
15 Java Platform Module System
16 Die Klassenbibliothek
17 Einführung in die nebenläufige Programmierung
18 Einführung in Datenstrukturen und Algorithmen
19 Einführung in grafische Oberflächen
20 Einführung in Dateien und Datenströme
21 Einführung ins Datenbankmanagement mit JDBC
22 Bits und Bytes, Mathematisches und Geld
23 Testen mit JUnit
24 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 18 Einführung in Datenstrukturen und Algorithmen
Pfeil 18.1 Listen
Pfeil 18.1.1 Erstes Listen-Beispiel
Pfeil 18.1.2 Auswahlkriterium ArrayList oder LinkedList
Pfeil 18.1.3 Die Schnittstelle List
Pfeil 18.1.4 ArrayList
Pfeil 18.1.5 LinkedList
Pfeil 18.1.6 Der Array-Adapter Arrays.asList(…)
Pfeil 18.1.7 ListIterator *
Pfeil 18.1.8 toArray(…) von Collection verstehen – die Gefahr einer Falle erkennen
Pfeil 18.1.9 Primitive Elemente in Datenstrukturen verwalten
Pfeil 18.2 Mengen (Sets)
Pfeil 18.2.1 Ein erstes Mengen-Beispiel
Pfeil 18.2.2 Methoden der Schnittstelle Set
Pfeil 18.2.3 HashSet
Pfeil 18.2.4 TreeSet – die sortierte Menge
Pfeil 18.2.5 Die Schnittstellen NavigableSet und SortedSet
Pfeil 18.2.6 LinkedHashSet
Pfeil 18.3 Assoziative Speicher
Pfeil 18.3.1 Die Klassen HashMap und TreeMap
Pfeil 18.3.2 Einfügen und Abfragen des Assoziativspeichers
Pfeil 18.4 Java-Stream-API
Pfeil 18.4.1 Deklaratives Programmieren
Pfeil 18.4.2 Interne versus externe Iteration
Pfeil 18.4.3 Was ist ein Stream?
Pfeil 18.5 Einen Stream erzeugen
Pfeil 18.5.1 Parallele oder sequenzielle Streams
Pfeil 18.6 Terminale Operationen
Pfeil 18.6.1 Die Anzahl der Elemente
Pfeil 18.6.2 Und jetzt alle – forEach*(…)
Pfeil 18.6.3 Einzelne Elemente aus dem Strom holen
Pfeil 18.6.4 Existenztests mit Prädikaten
Pfeil 18.6.5 Einen Strom auf sein kleinstes bzw. größtes Element reduzieren
Pfeil 18.6.6 Einen Strom mit eigenen Funktionen reduzieren
Pfeil 18.6.7 Ergebnisse in einen Container schreiben, Teil 1: collect(…)
Pfeil 18.6.8 Ergebnisse in einen Container schreiben, Teil 2: Collector und Collectors
Pfeil 18.6.9 Ergebnisse in einen Container schreiben, Teil 3: Gruppierungen
Pfeil 18.6.10 Stream-Elemente in ein Array oder einen Iterator übertragen
Pfeil 18.7 Intermediäre Operationen
Pfeil 18.7.1 Element-Vorschau
Pfeil 18.7.2 Filtern von Elementen
Pfeil 18.7.3 Statusbehaftete intermediäre Operationen
Pfeil 18.7.4 Präfix-Operation
Pfeil 18.7.5 Abbildungen
Pfeil 18.8 Zum Weiterlesen
 

Zum Seitenanfang

18.3    Assoziative Speicher Zur vorigen ÜberschriftZur nächsten Überschrift

Ein assoziativer Speicher verbindet einen Schlüssel mit einem Wert. Java bietet für Datenstrukturen dieser Art die allgemeine Schnittstelle Map mit wichtigen Operationen wie put(key, value) zum Aufbau einer Assoziation und get(key) zum Erfragen eines assoziierten Werts.

 

Zum Seitenanfang

18.3.1    Die Klassen HashMap und TreeMap Zur vorigen ÜberschriftZur nächsten Überschrift

Die Java-Bibliothek implementiert assoziativen Speicher mit einigen Klassen, wobei wir unser Augenmerk zunächst auf zwei wichtige Klassen richten wollen:

  • Eine schnelle Implementierung ist die Hash-Tabelle (engl. hashtable), die in Java durch java.util.HashMap implementiert ist. Vor Java 1.2 wurde java.util.Hashtable verwendet. Die Schlüsselobjekte müssen »hashbar« sein, also equals(…) und hashCode() konkret implementieren. Eine besondere Schnittstelle für die Elemente ist nicht nötig.

  • Daneben existiert die Klasse java.util.TreeMap, die etwas langsamer im Zugriff ist, doch dafür alle Schlüsselobjekte immer sortiert hält. Sie sortiert die Elemente in einen internen Binärbaum ein. Die Schlüssel müssen sich in eine Ordnung bringen lassen, wozu etwas Vorbereitung nötig ist.

[»]  Beispiel

Ein Assoziativspeicher, dem wir Werte hinzufügen:

Map<String,String> aldiSupplier = new HashMap<>();

aldiSupplier.put( "Carbo, spanischer Sekt", "Freixenet" );

aldiSupplier.put( "ibu Stapelchips", "Bahlsen Chipsletten" );

aldiSupplier.put( "Ko-kra Katzenfutter", "felix Katzenfutter" );

aldiSupplier.put( "Küchenpapier", "Zewa" );

aldiSupplier.put( "Nuss-Nougat-Creme", "Zentis" );

aldiSupplier.put( "Pommes Frites", "McCain" );

Die zweite HashMap soll Strings mit Zahlen assoziieren:

Map<String,Number> num = new HashMap<>();

num.put( "zwei", 2 ); // Boxing durch Integer.valueOf(2)

num.put( "drei", 3.0 ); // Boxing durch Double.valueOf(3.0)

Während also bei den Assoziativspeichern nach dem Hashing-Verfahren eine hashCode()- und equals(…)-Methode bei den Schlüssel-Objekten essenziell ist, ist das bei den baumorientierten Verfahren nicht nötig – hier muss nur eine Ordnung zwischen den Elementen her, entweder mit Comparable oder Comparator.

Ein Assoziativspeicher arbeitet nur in eine Richtung schnell. Wenn etwa im Fall eines Telefonbuchs ein Name mit einer Nummer assoziiert wurde, kann die Datenstruktur die Frage nach einer Telefonnummer schnell beantworten. In die andere Richtung dauert es wesentlich länger, weil hier keine Verknüpfung besteht. Sie ist immer nur einseitig. Auf wechselseitige Beziehungen sind die Klassen nicht vorbereitet.

UML-Klassendiagramm der Schnittstelle Map

Abbildung 18.2     UML-Klassendiagramm der Schnittstelle Map

Die Klasse HashMap

Die Klasse HashMap eignet sich ideal dazu, viele Elemente unsortiert zu speichern und sie über die Schlüssel schnell wieder verfügbar zu machen. Das interne Hashing-Verfahren ist schnell, eine Sortierung der Schlüssel nach einem gegebenen Kriterium aber nicht möglich, ein Iterator wird auch eine undurchsichtbare Reihenfolge liefern.

class java.util.HashMap<K,V>

extends AbstractMap<K,V>

implements Map<K,V>, Cloneable, Serializable
  • HashMap()

    Erzeugt eine neue Hash-Tabelle.

  • HashMap(Map<? extends K,? extends V> m)

    Erzeugt eine neue Hash-Tabelle aus einer anderen Map.

Die Klasse TreeMap und die Schnittstelle SortedMap/NavigableMap

Eine TreeMap implementiert die Schnittstelle NavigableMap, die wiederum von der Schnittstelle SortedMap erbt, wobei diese wiederum Map erweitert. Eine NavigableMap sortiert die Elemente eines Assoziativspeichers nach Schlüsseln und bietet Zugriff auf das kleinste oder größte Element.

  • Einige Methoden aus SortedMap: firstKey(), lastKey(). subMap(fromKey, toKey) und tailMap(fromKey, toKey) bilden Teilansichten des Assoziativspeichers.

  • einige Methoden aus NavigableMap: pollFirstEntry(), pollLastEntry(), descendingMap()

Damit die Schlüssel in einer TreeMap sortiert werden können, gilt das Gleiche wie beim TreeSet: Die Elemente müssen eine natürliche Ordnung besitzen, oder ein externer Comparator muss die Ordnung festlegen.

class java.util.TreeMap<K,V>

extends AbstractMap<K,V>

implements NavigableMap<K,V>, Cloneable, Serializable
  • TreeMap()

    Erzeugt eine neue TreeMap, die eine natürliche Ordnung von ihren Elementen erwartet.

  • TreeMap(Comparator<? super K> comparator)

    Erzeugt eine neue TreeMap mit einem Comparator, sodass die Elemente keine natürliche Ordnung besitzen müssen.

  • TreeMap(Map<? extends K,? extends V> m)

    Erzeugt eine TreeMap mit einsortierten Elementen aus m, die eine natürliche Ordnung besitzen müssen.

  • TreeMap(SortedMap<K,? extends V> m)

    Erzeugt eine TreeMap mit einsortierten Elementen aus m und übernimmt von m auch die Ordnung.

Um die Sortierung zu ermöglichen, ist der Zugriff etwas langsamer als über HashMap, aber mit dem Hashing-Verfahren lassen sich Elemente nicht sortieren.

 

Zum Seitenanfang

18.3.2    Einfügen und Abfragen des Assoziativspeichers Zur vorigen ÜberschriftZur nächsten Überschrift

Wir haben gesagt, dass die Elemente des Assoziativspeichers Paare aus Schlüssel und zugehörigem Wert sind. Das Wiederfinden der Werte ist effizient nur über Schlüssel möglich.

Daten einfügen

Zum Hinzufügen von Schlüssel-Wert-Paaren dient die Methode put(key, value). Das erste Argument ist der Schlüssel und das zweite Argument der mit dem Schlüssel zu assoziierende Wert.

[+]  Hinweis für Nullen

Bei manchen Implementierungen der Map-Schnittstelle kann der Schlüssel bzw. Wert null sein – hier lohnt ein Blick auf die Javadoc der einzelnen Klassen. Bei HashMap ist null als Schlüssel und Wert ausdrücklich erlaubt, bei ConcurrentHashMap dürfen weder Schlüssel noch Wert null sein.

interface java.util.Map<K,V>
  • V put(K key, V value)

    Speichert den Schlüssel und den Wert im Assoziativspeicher. Falls sich zu diesem Schlüssel schon ein Eintrag im Assoziativspeicher befand, wird der alte Wert überschrieben und der vorherige Wert zum Schlüssel zurückgegeben (das ist anders als bei Set, wo die Operation dann nichts tut). Ist der Schlüssel neu, liefert put(…) den Rückgabewert null. Das heißt auch, dass mit put(key, value) == null nicht klar ist, ob put(…) einen Wert überschreibt und der alte Wert null war oder ob noch kein Schlüssel-Wert-Paar in dem Assoziativspeicher lag.

  • void putAll(Map<? extends K,? extends V> m)

    Fügt alle Schlüssel-Wert-Paare aus m in die aktuelle Map ein. Auch diese Methode überschreibt unter Umständen vorhandene Schlüssel.

Über alle Werte laufen

Die Default-Methode forEach(java.util.function.BiConsumer) läuft über alle Schlüssel-Wert-Paare und ruft einen BiConsumer auf – das ist eine funktionale Schnittstelle mit einer Methode, die zwei Parameter bekommt, nämlich Schüssel und Wert. Der Konsument kann die Daten dann auswerten.

[»]  Beispiel

Die Ausgabe wird sein: »zwei=2« und »drei=3.0«.

Map<String,Number> num = new HashMap<>();

num.put( "zwei", 2 );

num.put( "drei", 3.0 );

BiConsumer<String,Number> action =

(key, value) -> System.out.println( key + "=" + value );

num.forEach( action );

Assoziierten Wert erfragen

Um wieder ein Element auszulesen, deklariert Map die Operation get(key). Das Argument identifiziert das zu findende Objekt über den Schlüssel, indem dasjenige Objekt aus der Datenstruktur herausgesucht wird, das im Sinne von equals(…) mit dem Abfrageobjekt gleich ist. Wenn das Objekt nicht vorhanden ist, ist die Rückgabe null. Allerdings kann auch null der mit einem Schlüssel assoziierte Wert sein, da null als Wert durchaus erlaubt ist.

[»]  Beispiel

Erfrage den Assoziativspeicher nach »zwei«. Das Ergebnis wird ein Number-Objekt sein:

Map<String,Number> num = new HashMap<>();

Number number = num.get( "zwei" );

if ( number != null )

System.out.println( number.intValue() );

Mit Generics kann eine Typumwandlung entfallen, wenn – wie in unserem Beispiel – Number-Objekte mit dem String assoziiert waren. Wurde der Typ nicht angegeben, ist eine Typumwandlung nötig.

interface java.util.Map<K,V>
  • V get(Object key)

    Liefert das mit dem entsprechenden Schlüssel verbundene Objekt. Falls kein passendes Objekt vorhanden ist, liefert die Methode null.

  • default V getOrDefault(Object key, V defaultValue)

    Existiert zum Schlüssel key ein assoziierter Wert, so wird dieser zurückgegeben, andernfalls der vorgegebene defaultValue.

Existiert der Schlüssel, existiert der Wert?

Mit get(…) kann nicht wirklich sicher das Vorhandensein eines Schlüssels getestet werden, da mit einem Schlüssel null assoziiert sein kann – das gilt zum Beispiel für HashMap, in der null als Schlüssel und Wert erlaubt sind. Eine sichere Alternative bietet die Methode containsKey(…), die true zurückgibt, wenn ein Schlüssel in der Tabelle vorkommt.

Im Gegensatz zu get(…) und containsKey(…), die das Auffinden eines Werts bei gegebenem Schlüssel erlauben, lässt sich auch nur nach den Werten ohne Schlüssel suchen. Dies ist allerdings wesentlich langsamer, da alle Werte der Reihe nach durchsucht werden müssen. Die Klasse bietet hierzu containsValue(…) an. Wir können uns auch alle assoziierten Werte geben lassen und dann Prüfungen durchführen.

interface java.util.Map<K,V>
  • boolean containsKey(Object key)

    Liefert true, falls der Schlüssel im Assoziativspeicher vorkommt. Den Vergleich auf Gleichwertigkeit führt HashMap mit equals(…) durch. Demnach sollte das zu vergleichende Objekt diese Methode aus Object passend überschreiben. hashCode() und equals(…) müssen miteinander konsistent sein. Aus der Gleichwertigkeit zweier Objekte unter equals(…) muss auch jeweils die Gleichheit von hashCode() folgen.

  • boolean containsValue(Object value)

    Liefert true, falls der Assoziativspeicher einen oder mehrere Werte enthält, die mit dem Objekt value inhaltlich (also per equals(…)) übereinstimmen.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Spring Boot 3 und Spring Framework 6

Spring Boot 3 und Spring Framework 6




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2024

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.

Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de



Cookie-Einstellungen ändern