List
Das Interface List.
List
, Set
, Queue
, Map
.ArrayList
, HashSet
, HashMap
.ArrayList
.HashSet
.HashMap
.Stack
.Queue
und Deque
und kann diese anwenden.LinkedList
.Beim Programmieren müssen wir oft Daten speichern bzw. Informationen verwalten, um gewisse Probleme zu lösen. In diesem Modul werden wir das Java Collections Framework anschauen, weil dieses Framework uns die Werkzeuge zum effizienten Verwalten von Informationen liefert.
Eine Collection ist ein Objekt, das eine Sammlung von Objekten darstellt, d.h. mehrere Elemente zu einer Einheit zusammenfasst. In der Regel enthält eine Collection Datenelemente, die zusammen eine natürliche Gruppe bilden, wie z.B. eine Fussballmannschaft, die eine “Sammlung” von Fussballspielern ist, d.h. Fussballspieler enthält. Collections bieten uns im Allgemeinen die Möglichkeit neue Elemente hinzuzufügen, Elemente zu löschen und sonst die Elemente zu verwalten.
Ein bekanntes Beispiel für eine Collection ist die ArrayList
Klasse, wobei eine ArrayList eine Liste von Objekten
darstellt, welche skalierbar ist. Die ArrayList
Klasse liefert uns beispielsweise die Methode add
, mit welcher
Elemente an das Ende einer Liste angefügt werden kann:
|
|
Oder sie liefert uns die Methode remove
, welche Elemente aus der Liste entfernt:
|
|
Wir werden die ArrayList Klasse später noch genauer anschauen.
Das Java Collections Framework ist eine Menge von Interfaces und Klassen, die allgemein wiederverwendbare Collection-Datenstrukturen liefern. Es bietet uns also sowohl Interfaces, die Collection-Typen definieren, als auch Klassen, die diese implementieren an. Obwohl es als Framework bezeichnet wird, funktioniert es im Grunde wie eine Library.
Das Java Collections Frameworks stellt für uns Hochleistungsimplementierungen von Datenstrukturen und Algorithmen bereit, um Sammlungen von Objekten beliebiger Datentypen darzustellen. Da wir diese Funktionalität nicht immer selbst programmieren müssen, reduziert sich für uns der Programmieraufwand markant.
Das Java Collections Framework befindet sich im Paket java.util
.
Wir haben die ArrayList Klasse (java.util.ArrayList
) als Beispiel für eine Collection gesehen. Die ArrayList Klasse
repräsentiert eine Collection vom Typ List (implementiert also das Interface java.util.List
) und wird mithilfe von
zugrunde liegenden Arrays implementiert, deshalb auch der Name ArrayList. Es gibt aber auch andere Klassen, die Collections vom Typ List
darstellen. Wir werden später einige davon noch kennenlernen.
Das Java Collections Framework ist eine einheitliche Architektur zur Darstellung und Bearbeitung von Collections, welche folgendes enthält:
Interfaces: Dies sind abstrakte Datentypen, die verschiedene Collections darstellen. Mithilfe von Interfaces können Collections unabhängig von den Details ihrer Implementierung bearbeitet werden. Die Interfaces bilden in Java die Hierarchie aller Collections.
Implementierungen/Klassen: Dies sind die konkreten Implementierungen der Collection-Interfaces. Im Grunde handelt es sich um wiederverwendbare Datenstrukturen mit konkreten Implementierungen.
Algorithmen/Methoden: Dies sind die Methoden, die nützliche Algorithmen, wie z. B. Hinzufügen, Löschen, Suchen und Sortieren, von Objekten in Collections durchführen. Viele Methoden und Algorithmen sind für verschiedene Arten der Collections wiederverwendbar.
Die Interfaces in der folgenden Abbildung (Collection
, Set
, List
, Queue
, Deque
, Map
…)
bilden die Grundlage des Collection Frameworks. Durch diese grundlegenden Interfaces bildet sich eine Hierarchie
innerhalb des Collection Frameworks:
Auf dieser Grafik ist zu sehen, dass zum Beispiel:
In der Abbildung sieht man zudem, dass das Collection Framework aus zwei verschiedenen Teilen besteht: Zum einen die Collections und zum anderen die Maps. Maps stellen somit keine “echten” Collections dar. Maps sind trotzdem Datenstrukturen zur Darstellung von Sammlungen von Objekten als eine Einheit.
Eine Collection
ist ein Objekt, dass eine Sammlung von Objekten darstellt, d.h. mehrere Elemente zu einer Einheit zusammenfasst.
In der Regel enthält eine Collection Datenelemente, die zusammen eine natürliche Gruppe bilden, wie z.B. eine Fussballmannschaft, die eine “Sammlung” von Fussballspielern ist, d.h. Fussballspieler enthält.
Collections bieten uns im Allgemeinen die Möglichkeit neue Elemente hinzuzufügen, Elemente zu löschen und sonst die Elemente zu verwalten.
Allgemeine Methoden:size()
, isEmpty()
, contains(Object element)
, add(E element)
, remove(Object element)
, clear()
, iterator()
Sammelmethoden:containsAll(Collection<?> c)
, addAll(Collection<? extends E> c)
, removeAll(Collection<?> c)
, retainAll(Collection<?> c)
Eine List ist eine geordnete Sequenz, welche duplizierte Elemente erlaubt.
Zusätzlich zu den geerbten Methoden der Collection
bietet die List
folgende an:
Elemente auf Basis ihrer Position zugreifen:get
, set
Suche nach einem bestimmten Element in der Liste:indexOf
, lastIndexOf
Iteriert durch die Liste:listIterator
Ein Teilbereich der Liste erstellen:sublist
Folgend sind zwei gängige
List
Implementierungen:
- ArrayList welche in Normalfall die leistungsfähigere Implementation ist. Sie basiert auf einem primitiven Array.
- LinkedList welche in bestimmten Situation die effizientere Lösung sein kann.
Ein Set ist eine Collection, in welche man ein Element nur einmal hinzufügen kann.
Das Set enthält die Funktionen der Collection, stellt aber sicher, dass Elemente nicht doppelt vorkommen können.
Die equals
und hashCode
Funktionen spielen dabei eine wichtige Rolle.
Sie definieren, wann zwei Elemente als gleich gelten.
Elemente hinzufügen, löschen und Infos abfragen:add()
, contains()
, remove()
, clear()
, size()
, isEmpty()
Sammelmethoden:addAll()
, removeAll()
, containsAll()
Zugriff wie auf eine Collection:iterator()
Folgend sind 3 gängige
Set
Implementationen:HashSet - Schnell, aber unsortiert
- Speichert Elemente in keiner bestimmten Reihenfolge.
- Sehr schnell beim Einfügen und Suchen.
TreeSet – Sortiert, aber langsamer
- Speichert Elemente aufsteigend sortiert.
- Einfügen und Suchen dauert länger.
LinkedHashSet – Reihenfolge bleibt erhalten
- Speichert Elemente in der Reihenfolge, in der sie hinzugefügt wurden.
- Schneller als TreeSet, aber etwas langsamer als HashSet.
Eine Map ist ein Objekt, dass Schüssel (keys) auf Werte (values) zuordnet. Eine Map kann nicht zwei gleiche Schlüssel enthalten. Jeder Schlüssel zeigt genau auf einen Wert. Das Interface Map definiert Grundfunktionen für das Einfügen, Lesen, Löschen, Abfragen von Schlüsseln usw.
Zusätzlich zu den vererbten Methoden der Collection
bietet die List
folgende an:
Elemente auf Basis ihrer Position zugreifen:put()
, get()
, containsKey()
, containsValue()
Sammelmethoden:putAll()
Zugriff wie auf eine Collection:keySet()
, entrySet()
, values()
Folgend sind 3 gängige Implementationen von
Map
:HashMap – Schnell, aber unsortiert
- Speichert Schlüssel-Wert-Paare in keiner bestimmten Reihenfolge.
- Sehr schnell beim Einfügen und Suchen.
TreeMap – Sortiert, aber langsamer
- Speichert Schlüssel nach ihrer natürlichen Ordnung oder mit einem Comparator.
- Einfügen und Suchen dauert länger.
LinkedHashMap – Reihenfolge bleibt erhalten
- Speichert Schlüssel in der Reihenfolge, in der sie hinzugefügt wurden.
- Schneller als
TreeMap
, aber etwas langsamer alsHashMap
.
Das Collections-Framework nutzt intensiv die Hash-Funktion.
Klassen wie HashSet
oder HashMap
verwenden die Hash-Funktion zur Steigerung der Performanz.
Alle Java Klassen erben von der Klasse java.lang.Object
die Methode public int hashCode()
.
Diese liefert ein Hash Code von der eigenen Instanz zurück.
Bei Java ist dieser ein Integer
.
Hashing bezeichnet die Umwandlung einer Zeichenfolge in einen numerischen Wert oder Schlüssel mit fester Länge, der in der Regel kürzer ist. Das bedeutet also, aus einem Java Objekt mit verschiedenen Attributen wird eine Zahl berechnet. Immer, wenn das gleiche Objekt wieder berechnet wird, erhält man die gleiche Zahl.
Der Java Hash Code ist nicht immer eindeutig. Es kann also vorkommen, dass unterschiedliche Instanzen verschiedener Klassen denselben Hash-Code zurückgeben. In der Praxis ist das jedoch kein Problem, da der Hash-Code lediglich für eine Vorselektion genutzt wird. Das bedeutet, wenn die Hash Codes nicht gleich sind, sind die Objekte definitiv nicht gleich. Sind die Hash Codes gleich, werden trotzdem noch die Objekte direkt vergleicht.
Schauen wir uns die Verwendung von Set
an:
Bei einem Set können wir mit der Methode contains(Object o)
abfragen, ob ein Objekt in einem Set vorhanden ist.
Das Set muss somit jedes Objekt mit dem Objekt vergleichen, welches wir der Methode contains
übergeben.
Falls ein Objekt jedoch viele Instanzvariablen (oder Attribute) besitzt – die wiederum Objekte sein können –,
wäre ein direkter Vergleich potenziell aufwendig.
Set
ist das vernachlässigbar.Die Klasse HashSet
verfolgt daher eine optimierte Strategie:
HashSet
dessen Hash-Code mit der Methode hashCode().
Dieser Hash-Code wird gespeichert.contains(Object o)
wird der Hash-Code des übergebenen Vergleichsobjekts berechnet.
Anschliessend wird dieser mit den gespeicherten Hash-Codes verglichen (Integer-Vergleich).equals(Object o)
,
um sicherzustellen, dass es sich tatsächlich um dasselbe Objekt handelt.Mit der Hash-Code Strategie kann das
HashSet
die allermeisten Vergleiche auf einen Integer-Vergleich reduzieren.
Was ist die Anforderung an die Java hashCode()
Methode?
- Die Berechnung muss schnell sein.
- Der Hash Code sollte in der Praxis meistens eindeutig sein.
Das Interface List.
Eine konkrete Umsetzung einer List: Die ArrayList.
Die gebräuchlichste Umsetzung eines Set: HashSet.
Die gebräuchlichste Umsetzung einer Map: HashMap.
Die Datenstruktur “Stapel”.
Die Datenstruktur für eine Warteschlange: die Queue.
Eine verkettete Liste, die LinkedList.
Modul #J6