Java ArrayList: flexible Datenstruktur für eine unterschiedliche Anzahl an Inhalten
Bei den meisten Computerprogrammen ist es notwendig, mehrere Werte in einer einheitlichen Struktur abzuspeichern. Das macht die Verwaltung der Inhalte deutlich leichter, als wenn Sie dafür einzelne Variablen verwenden. Die grundlegende Datenstruktur in Java ist hierbei das Array – genau wie in vielen weiteren Programmiersprachen. Allerdings gibt es noch viele weitere Möglichkeiten, die in verschiedenen Anwendungsfällen erhebliche Vorteile bieten können. Eine ebenfalls sehr beliebte Datenstruktur ist die ArrayList. Diese soll in diesem Artikel vorgestellt werden.
Listen und Arrays: Worin bestehen die Unterschiede?
Bevor wir uns damit befassen, wie Sie die ArrayList anwenden können, ist es wichtig, sich zunächst kurz zu überlegen, in welchen Fällen deren Verwendung sinnvoll ist. Wie bereits in der Einleitung dargestellt kommt für die Aufnahme mehrerer zusammengehöriger Werte in Java häufig ein Array zum Einsatz. Bei einem Array handelt es sich um einen zusammengehörigen Bereich im Arbeitsspeicher. Um damit zu arbeiten, muss das Programm den Startpunkt des Speicherbereichs und die Länge des Arrays kennen. Ein weiterer wichtiger Aspekt besteht im verwendeten Datentyp. Da verschiedene Datentypen eine unterschiedliche Größe aufweisen können, sind dafür auch verschiedengroße Arrayfelder erforderlich. Wenn all diese Informationen vorhanden sind, ist der Zugriff auf die einzelnen Inhalte jedoch einfach und effizient. Dazu muss das Programm zum jeweiligen Startpunkt des Arrays die Position des gewünschten Feldes hinzuzählen – multipliziert mit der Größe des entsprechenden Datentyps. Die hohe Effizienz der Arrays bietet insbesondere bei Programmen, die größeren Datenmengen bearbeiten, erhebliche Vorteile.
Dem Vorteil der hohen Effizienz steht jedoch auch ein entscheidender Nachteil gegenüber: Arrays haben stets eine feste Größe. Das bedeutet, dass bereits bei der Erstellung des Programms genau klar sein muss, wie viele Felder das Array aufweisen soll. Das stellt jedoch eine erhebliche Einschränkung dar. Oftmals ergibt sich die Zahl der benötigten Felder erst aus den Eingaben des Anwenders oder kann sich im Laufe des Programms ändern. Deshalb ist es hierbei sinnvoll, eine andere Datenstruktur zu verwenden. Hierfür bietet sich die ArrayList an. Diese besteht nicht aus einem zusammenhängenden Speicherbereich. Das führt dazu, dass für den Zugriff auf die einzelnen Felder in der Regel etwas mehr Zeit erforderlich ist. Dafür lässt sich die ArrayList beliebig erweitern und bietet uns daher eine hohe Flexibilität.
Die ArrayList im Java Collections Framework
Die ArrayList ist Teil des Java Collections Frameworks. Hierbei handelt es sich um eine Sammlung von Klassen und Interfaces, die verschiedene Muster für zusammengesetzte Datenstrukturen anbieten. Beim Collections Framework handelt es sich jedoch um eine etwas neuere Entwicklung, die ursprünglich nicht verfügbar war. Sie wurde erst mit der JDK-Version 1.2 eingeführt, die 1998 veröffentlicht wurde. Doch auch zuvor gab es bereits Möglichkeiten für die Verwendung von Listen und weiteren zusammengesetzten Datentypen in Java. Die Implementierung war dabei jedoch sehr uneinheitlich, sodass die Entwickler der Programmiersprache beschlossen, mit dem Java Collections Framework einheitliche Strukturen dafür zu schaffen. In älteren Programmbeispielen und Lehrbüchern findet man jedoch manchmal noch andere Möglichkeiten, um Listen zu gestalten.
Eine ArrayList erzeugen
Wenn Sie mit einer ArrayList arbeiten möchten, müssen Sie beachten, dass diese Klasse nicht im Standardwortschatz der Programmiersprache enthalten ist. Sie befindet sich in der Bibliothek java.util. Das bedeutet, dass Sie diese zunächst importieren müssen, bevor Sie eine ArrayList erzeugen können. Wenn Sie lediglich mit diesem Datentyp arbeiten wollen, können Sie direkt die entsprechende Klasse importieren:
import java.util.ArrayList;
Eine weitere Möglichkeit besteht darin, die komplette Bibliothek zu importieren. Das bietet sich insbesondere dann an, wenn Sie noch weitere darin definierte Datentypen verwenden möchten. Dafür können Sie den folgenden Befehl verwenden:
import java.util.*;
Nach dieser Vorarbeit können Sie die Liste im Hauptprogramm erzeugen. Dafür ist es zunächst notwendig, den Namen der Datenstruktur zu nennen – also ArrayList. Danach folgt in einer spitzen Klammer der Datentyp, den Sie hier aufnehmen möchten. Daraufhin müssen Sie einen Namen für die Liste vorgeben. Nach dem Zuweisungsoperator ist es dann erforderlich, eine neue Liste zu erzeugen. Wie bei Objekten benötigen Sie hierfür den Schlüsselbegriff new. Das liegt daran, dass es sich bei der ArrayList ebenfalls um ein Objekt handelt. Danach folgt eine weitere spitze Klammer. In vielen Programmbeispielen sieht man, dass hier der Datentyp wiederholt wird. Das ist jedoch nicht notwendig. Um den Code so kompakt wie möglich zu halten, verzichten wir darauf und fügen lediglich eine leere spitze Klammer ein. Daran schließt sich eine leere runde Klammer an. Wenn Sie beispielsweise eine Liste für Städtenamen erstellen möchten, müssen Sie hierfür den Datentyp String verwenden. Die Liste erzeugen Sie dann mit dem folgenden Befehl:
ArrayList<String> Staedte = new ArrayList<>();
Für welche Datentypen eignet sich die ArrayList?
Wenn Sie eine ArrayList erzeugen, ist es notwendig, den Datentyp dafür vorzugeben. Es ist hierbei jedoch wichtig, darauf zu achten, dass Sie hierbei nur Objekte verwenden können. Primitive Datentypen wie int, double oder char sind hingegen nicht zulässig. Das stellt einen weiteren wichtigen Unterschied zu Arrays dar. Diese Vorgabe wirkt auf den ersten Blick wie eine erhebliche Einschränkung – schließlich dienen viele Listen dazu, einfache Zahlen aufzunehmen. Doch gibt es auch hierfür eine Lösung. In Java sind für alle primitiven Datentypen auch passende Klassen implementiert – beispielsweise Integer, Double oder Character. Diese dienen lediglich dazu, einen primitiven Datentyp aufzunehmen. Die Verwendung dieser Objekte ermöglicht es, auch Listen für einfache Werte zu erstellen. Dennoch müssen Sie hierbei darauf achten, bei der Angabe des Datentyps die entsprechende Klasse zu verwenden. Diese müssen Sie stets mit einem Großbuchstaben schreiben.
Methoden für die Arbeit mit Listen
Um mit der ArrayList zu arbeiten, stehen uns verschiedene Methoden zur Verfügung. Diese erlauben es beispielsweise, Werte einzufügen, abzurufen und zu verändern. Außerdem können Sie auf diese Weise die Länge einer Liste herausfinden oder ermitteln, ob sie leer ist. Darüber hinaus gibt es jedoch noch zahlreiche weitere Möglichkeiten. Im Folgenden stellen wir Ihnen die wichtigsten Methoden für ArrayLists vor.
add() und addAll()
Von besonderer Bedeutung bei der Arbeit mit einer ArrayList ist die Methode add(). Diese dient dazu, ein Element am Ende der Liste einzufügen. Dazu müssen Sie dieses lediglich in die Klammer stellen. Wenn Sie beispielsweise eine Stadt in unsere Städteliste einfügen möchten, ist dies mit dem folgenden Befehl möglich:
Staedte.add("Berlin");
Hierbei haben Sie außerdem die Möglichkeit, eine Position vorzugeben. Diese müssen Sie dann an erster Stelle in die Klammer einfügen. Nach einem Komma folgt dann der gewünschte Wert. Dies soll nun an einem kleinen Beispiel vorgeführt werden. Dieses trägt zunächst vier Städte in die Liste ein und gibt diese dann aus. Hierfür müssen Sie lediglich den Namen der Liste in den println-Befehl schreiben. Danach fügen wir eine weitere Stadt in die Liste ein – dieses Mal jedoch mit der Vorgabe einer Position. Danach geben wir die Liste erneut aus:
An diesem Beispielprogramm sehen Sie, dass die Positionen der ArrayList genau wie bei Arrays mit dem Wert 0 beginnen. Wenn Sie wie in diesem Beispiel die Position 3 vorgeben, wird die entsprechende Stadt an vierter Stelle eingefügt.
Darüber hinaus gibt es eine weitere Methode, um Elemente in die Liste einzufügen: addAll(). Diese bietet sich an, wenn Sie bereits eine andere Datenstruktur erstellt haben, die im Java Collections Framework definiert ist. Diese können Sie mit dieser Methode komplett am Ende der Liste einfügen.
get()
Wenn Sie auf ein bestimmtes Element der Liste zugreifen möchten, können Sie hierfür die get()-Methode verwenden. In der Klammer steht die Indexnummer des Feldes, das Sie aufrufen möchten. Wenn Sie beispielsweise den Namen einer bestimmten Stadt ausgeben möchten, können Sie den folgenden Befehl dafür nutzen:
System.out.println(Staedte.get(2));
Selbstverständlich ist es mit dieser Methode auch möglich, den entsprechenden Inhalt in einer anderen Variable abzuspeichern:
String stadt = Staedte.get(2);
set()
Mit der set()-Methode ist es möglich, den Inhalt eines Felds zu verändern. Dazu müssen Sie an erster Stelle den Index des Feldes angeben, das Sie verändern möchten. Danach folgt der neue Wert, den es erhalten soll:
Staedte.set(1, "Düsseldorf");
remove() und clear()
Eine weitere wichtige Aufgabe besteht darin, ein Element aus der Liste zu löschen. Hierfür können Sie die Methode remove() verwenden. Diese bietet zwei verschiedene Anwendungsmöglichkeiten. Zum einen können Sie in die Klammer die Position des entsprechenden Elements schreiben. Möchten Sie beispielsweise die Stadt Hamburg aus der Liste löschen, die an zweiter Stelle steht, können Sie hierfür den folgenden Befehl verwenden:
Staedte.remove(1);
Eine weitere Möglichkeit stellt es dar, den Wert, den Sie löschen möchten, in die Klammer zu stellen – in diesem Fall also den Namen der Stadt:
Staedte.remove("Hamburg");
Hierbei müssen Sie jedoch einige Besonderheiten beachten. Wenn der entsprechende Wert mehrfach in der Liste auftaucht, löscht diese Funktion immer nur das erste Feld, in dem er auftritt. Auf besondere Weise stellt sich der Sachverhalt dar, wenn Sie eine Liste mit ganzen Zahlen verwenden. Hierbei kommt es zu einer Verwechslungsgefahr. Daher müssen Sie in diesem Fall beachten, dass sich die entsprechende Angabe immer auf die Position bezieht.
Darüber hinaus können Sie die komplette Liste löschen. Hierfür kommt die Methode clear() zum Einsatz:
Staedte.clear();
size()
In vielen Fällen ist es auch wichtig, die Größe der Liste zu bestimmen. Hierfür können Sie die Methode size() verwenden. Dabei ist es wieder möglich, den Wert direkt auszugeben oder in einer Variable zu speichern:
isEmpty()
Häufig ist es auch notwendig, zu ermitteln, ob eine Liste leer ist oder ob Inhalte darin vorhanden sind. Das wäre selbstverständlich mit der eben vorgestellten Methode size() möglich. Doch gibt es hierfür auch eine direktere Form: die Methode isEmpty(). Diese gibt einen booleschen Wert zurück. Diesen können Sie beispielsweise als Bedingung für eine if-Abfrage verwenden:
indexOf() und lastIndexOf()
Wenn Sie mit einer ArrayList arbeiten, können Sie auch den Index eines bestimmten Werts ermitteln. Dazu dient die Methode indexOf(). Wollen Sie beispielsweise in Ihrem Programm ausgeben, an welcher Position sich die Stadt Stuttgart in der Liste befindet, können Sie den folgenden Befehl dafür verwenden:
System.out.println("Position: " + Staedte.indexOf("Stuttgart"));
Selbstverständlich ist es auch bei dieser Methode möglich, den Rückgabewert einer Variablen zuzuweisen. Wenn ein Wert mehrfach in Ihrer Liste auftaucht, müssen Sie beachten, dass diese Funktion immer die Position angibt, an der er als Erstes auftritt. Alternativ dazu können Sie auch die Methode lastIndexOf() verwenden. Damit bestimmen Sie die letzte Position, an der der entsprechende Wert vorkommt.
toArray()
Manchmal ist es auch sinnvoll, ein Array zu erstellen, das die Inhalte der Liste aufnimmt. Zu diesem Zweck dient die Methode toArray(). Diese erzeugt eine Kopie der Liste und speichert diese in einem Array ab. Die ursprüngliche Liste bleibt dabei unverändert. Wenn Sie ein Array mit unseren Städten erstellen möchten, können Sie den folgenden Befehl dafür verwenden:
String staedteArray[] = Staedte.toArray(new String[0]);
Hierbei wirkt der Übergabewert auf den ersten Blick etwas seltsam. An dieser Stelle müssen Sie ein neues Array für String-Werte erzeugen und dieses an die Methode übergeben. Dabei handelt es sich allerdings um ein leeres Array – erkenntlich am verwendeten Wert 0. Diese Angabe ist notwendig, da die Methode dieses Array benötigt, um damit ein neues Array für die Aufnahme der Werte der ArrayList zu erstellen. Sollten die Liste statt String-Variablen andere Werte enthalten, ist es selbstverständlich notwendig, den übergebenen Array entsprechend anzupassen.
sort()
- Wenn Sie eine Liste erzeugt haben, können Sie diese auch sortieren – entweder alphabetisch oder anhand der enthaltenen nummerischen Werte. Hierfür kommt die Methode sort() zum Einsatz. Diese ist jedoch nicht in der Klasse ArrayList implementiert, sondern in der Bibliothek Collections. Daher müssen Sie diese entsprechend einbinden – entweder mit dem Befehl import java.util.Collections; oder indem Sie das Sternsymbol verwenden (import java.util.*;).
- Diese Methode müssen Sie dann über das Interface Collections aufrufen. In der Klammer steht der Name der Liste:
- Collections.sort(Staedte);
ArrayList und LinkedList im Vergleich
Nachdem Sie nun die wesentlichen Methoden für die Arbeit mit ArrayLists kennengelernt haben, ist es noch sinnvoll, diese Datenstruktur mit weiteren Formen der Liste zu vergleichen. Das Java Collections Framework kennt neben der ArrayList auch noch die LinkedList. Die Anwendung ist hierbei sehr ähnlich. Die meisten der vorgestellten Methoden sind auch für die LinkedList verfügbar. In den meisten Programmen können Sie sich daher frei entscheiden, welche Form der Liste Sie verwenden.
Obwohl hinsichtlich der Anwendung kaum Unterschiede bestehen, gibt es hinsichtlich der Implementierung erhebliche Differenzen. Während die ArrayList die Position aller Elemente abspeichert, enthält die LinkedList nur die Position des ersten Elements. Jedes Element enthält dann die Information, wo sich das nächste Feld der Liste befindet – beziehungsweise dass die Liste zu Ende ist, wenn es sich um das letzte Feld handelt.
Wenn Sie nun in einer LinkedList ein bestimmtes Feld aufrufen möchten, ist der Aufwand hierfür meistens recht groß: Sie müssen jedes einzelne Feld durchgehen, bis Sie die gewünschte Position erreicht haben. Bei größeren Listen ist das mit einem erheblichen Rechenaufwand verbunden. Daher ist die LinkedList meistens sehr ineffizient.
Dabei gibt es aber auch eine Ausnahme. Die ersten Felder der Liste erreichen Sie sehr schnell. Wenn Sie ein Programm erstellen, das häufig das erste Feld der Liste löscht oder verändert, die übrigen Bereiche aber unverändert lässt, kann die Verwendung der LinkedList sehr effizient sein. Daher ist es stets wichtig, sich genau zu überlegen, welche der Listen sich für Ihren Anwendungszweck besser eignet.