.

Das List Interface sorgt für die grundlegenden Eigenschaften von Listen. Dies ist ein häufig eingesetzter Collection Typ in der täglichen Arbeit mit Java. Sie dienen der Ansammlung von Datensätzen. Und erleichtern uns den Umgang damit.

Übersicht – List Interface

Grundsätzlich speichert eine Liste Elemente nach der eingefügten Reihenfolge. Die Liste verwaltet ihre Elemente mittels Indizes. Dieser Index ist immer ganzzahlig und Null basiert (erstes Element bei index 0, zweites Element bei Index 2 u.s.w.). Über diese Indizes können Elemente abgerufen, geändert, entfernt und hinzugefügt werden. Die folgende Tabelle zeigt eine beispielhafte Liste mit String Elementen:

ElementMehlZuckerEierButterSchokolade
Index01234

In einer Liste werden Objekte beliebigen Typs gespeichert. Primitive Typen werden automatisch in entsprechende Wrapper Typen konvertiert. Zum Beispiel ‚int‘ in ‚Integer‘. In der Liste sind doppelte Elemente erlaubt. Auch ’null‘ Einträge können erstellt sind zulässig.

Das folgende Klassendiagramm zeigt die primären Methoden, die List Interface definiert sind:

Das List Interface.

Die Liste bildet die Basisschnittstelle für alle Listentypen, und die Klassen ArrayList und LinkedList sind zwei gängige Implementierungen von List.

List Implementierungen

Hier werden nur die beiden meist verwendeten Implementierungen des List Interface betrachtet. Eine Auflistung aller bekannten Implementierungen findet sich in der Java List Interface Dokumentation.

ArrayList<E>

Die ArrayList ist eine Implementierung, die Elemente in einem Hintergrundarray speichert. Die Größe des Arrays wird automatisch erweitert, wenn beim Hinzufügen neuer Elemente zur Liste nicht genügend Platz vorhanden ist. Die Standardgröße kann festgelegt werde, indem beim Erstellen einer neuen ArrayList eine Anfangskapazität angeben wird.

  • Bietet eine konstante Zugriffszeit für die Operationen: size, isEmpty, get, set, iterator und listIterator.
  • Eine amortisierte konstante Zugriffszeit für die add Operation
  • Lineare Zeit für andere Operationen.

Daher kann diese Implementierung in Betracht gezogen werden, wenn ein schneller, zufälliger Zugriff auf die Elemente gewünscht wird.

LinkedList<E>

Die LinkedList ist eine Implementierung, die Elemente in einer doppelt verknüpften Listendatenstruktur speichert.

  • Bietet eine konstante Zeit zum Hinzufügen und Entfernen von Elementen am Ende der Liste.
  • Lineare Zeit für Operationen an anderen Positionen in der Liste.

Daher können wir die Verwendung einer LinkedList in Betracht ziehen, wenn das schnelle Hinzufügen und Entfernen von Elementen am Ende der Liste erforderlich ist.

Neben ArrayList und LinkedList ist die Vector Klasse eine Legacy-Collection und wurde später zur Implementierung der List-Schnittstelle nachgerüstet. Vector ist threadsicher, ArrayList und LinkedList jedoch nicht. Das folgende Klassendiagramm zeigt den Vererbungsbaum des List Interface:

Ausschnitt aus dem Collection Klassendiagramm.

Eine neue List erstellen

Es ist sinnvoll eine Liste immer Typ bezogen zu erstellen. Hier ein paar Beispiele:

List<Object> allJavaObjects = new ArrayList<Object>();
List<Integer> onlyIntegers = new ArrayList<Integer>();
List<String> onlyStrings = new LinkedList<String>();

Seit Java 7 ist es auch möglich beim erstellen einer Liste die generischen Parameter auf der rechten Seite weg zu lassen. Der Compiler kann den tatsächlichen Typparameter aus der Deklaration auf der linken Seite ableiten.

List<Object> allJavaObjects = new ArrayList<>();
List<String> onlyStrings = new LinkedList<>();

Java 9 macht es möglich eine Liste zu erstellen und sofort mittels einer Factory-Methode aus List mit Inhalt zu füllen. Diese Methode List.of(e1, e2, e3, …) liefert allerdings ein ‚immutable‘ Objekt zurück. Das bedeutet dass keine weiteren Elemente zu dieser Liste hinzugefügt werden können.

List<String> animals = List.of("Cat", "Dog", "Pig", "Cow");

Wenn eine ArrayList mittels leerem Konstruktor erstellt wird, wird diese mit einer Kapazität von Zehn initialisiert. Diese Kapazität wird beim hinzufügen von mehr Elementen als der aktuellen Kapazität immer wieder angepasst. Das schlägt sich bei großen Datenmengen natürlich auf die Performance nieder.
Weiß man vorher das in die neue Liste zum Beispiel circa 1000 Elemente eingefügt werden sollen empfiehlt es sich die Liste mit einer entsprechenden Startkapazität zu erstellen.

List<String> students = new ArrayList<>(1000);

Grundlegende Methoden des List Interface

Die am häufigsten genutzten Operationen auf einer Liste sind: hinzufügen, abrufen, updaten und löschen von Elementen. Dafür gibt es einer Reihe von Methoden.

Elemente hinzufügen

Die Methoden add(Object), add(index, Object) und addAll() sind zum hinzufügen von Elementen zu einer Liste da. Es können nur Elemente des generischen Typs der Liste zu dieser hinzu gefügt werden!

List<String> students = new ArrayList<>();
students.add("Lisa");  //OK
students.add("Anton"); //OK
students.add(123);     //Das führt zu einem Compiler Fehler 

Natürlich können auch Subtypen vom generischen Typ der Liste zu eben dieser hinzugefügt werden.

List<Number> numbers = new LinkedList<>();
numbers.add(new Integer(777));
numbers.add(new Float(2.67));
numbers.add(new Double(99.99));
numbers.add(new Long(1000000000));

Mit der obigen add(Object) Methode werden die Elemente immer am Ende der Liste hinzugefügt. Möchte man aber an zweiter Position eine Element einfügen hilft add(index, Object) weiter.

numbers.add(1, new Integer(678));

Aber auch Elemente einer existierenden Collection,egal ob Map, Set oder List, können alle mit einem mal einer Liste hinzugefügt werden.

students.addAll(moreStudents);

Oder alle nach einer bestimmten Position.

students.addAll(2, moreStudents);

Elemente abfragen

Die Methode get (index) wird verwendet, um ein Element an einem angegebenen Index aus der Liste abfragen.

String name = students.get(1); // "Anton"

Bei einer LinkedList ist es zusätzlich möglich das erste und letzte Element mittels speziellen Methoden abzufragen.

Number first = numbers.getFirst() // 777
Number last numbers.getLast() // 1000000000

Elemente updaten

Um Elemente einer liste mit neuem Inhalt zu füllen kommt die Metode set(index, element) zum Einsatz. Sie überschreibt das Element am angegebenen Index.

students.set(0, "Anna"); // aus Lisa wird Anna

Elemente entfernen

Um ein Element aus der Liste zu entfernen sind die Methoden remove(index) oder remove(Object) da. Die zweitere nutzt eine Objektreferenz um das zu löschende Objekt zu bestimmen.

students.remove(1); // "Anton" wird gelöscht

oder

students.remove("Anton");

Bei der remove(Object) Methode ist zu beachten das beim bestimmen des zu löschenden Objekt die equals() Methode zum Einsatz kommt. Bei selbst definierten Objekt Typen ist es daher wichtig diese equals() Methode passend zu implementieren.

Das löschen aller Elemente aus einer Liste funktioniert in einem Einzeihler mit der Methode clear().

numbers.clear();

Über eine List iterieren

Wie auch bei Arrays kann über eine Liste mit einer for Schleife iteriert werden.

for(String name : names){
  System.out.println(name);
}

Oder man nutzt dazu einen Iterator.

Iterator<String iterator = names.iterator();
while(iterator.hasNext()) {
  System.out.println(iterator.next());
}

Seit Java 8 geht es auch noch viel kürzer.

names.forEach(n -> System.out.println(n);

Elemente suchen

Um die Indexposition eines speziellen Element in einer Liste zu suchen oder um zu erfahren ob eine Element in einer Liste enthalten ist gibt es die folgenden Methoden:

  • boolean contains(Object) – gibt ‚true‘ zurück wenn das gesuchte Element in der Liste enthalten ist.
  • int indexOf(Object) – gibt den ersten Index des gesuchten Element zurück, oder -1 wenn das Element nicht gefunden wird.
  • int indexOf(Object) – gibt den letzten Index des gesuchten Element zurück, oder -1 wenn das Element nicht gefunden wird.
if (names.contains("Anton")) {
  System.out.println("Element found");
} else {
  System.out.println("Element not found");

Auch hier ist zu beachten das die Elemente der Liste mit dem gesuchten mittels der equals() Methode verglichen werden. Bei selbst definierten Objekttypen ist es daher nötig die equals() Methode passend zu implementieren.

Eine Liste sortieren

Lange Zeit war es der einfachste Weg die statische Methode Colletions.sort(List<E>) zum sortieren einer Liste zu nutzen. Diese Methode sortier die Liste in einer natürlichen Reihenfolge.

List<Integer> distances = new ArrayList<>();
distances.add(34);
distances.add(12);
distances.add(23);
System.out.println("before sorting: " + distances);
Collections.sort(distances);
System.out.println("after sorting: " + distances);

Ausgabe:

before sorting: [34, 12, 23];
after sorting: [12, 23, 34]

Damit das auch so wie gezeigt klappt muss der Objekttyp der in der Liste steckt aber das Interface Compareable und dessen Methode compareTo() implementieren.

Seit Java enthält das List Interface eine eigene sort() Methode. Wodurch man den Umweg über Collections nicht mehr benötigt.

distances.sort(null) // sortiert nach der natürlichen Ordnung

Ein Teilmenge aus einer Liste

Die Methode subList(fromIndex, toIndex) erlaubt es uns einen Ausschnitt aus einer Liste in einer Subliste darzustellen. Dabei gibt fromIndex das erste Element an das in die Subliste mit einbezogen werden soll und toIndex das erste Element hinter der Subliste die erstellt wird.

  • fromIndex -> inklusiv
  • toIndex -> exklusiv
List<String> names = Arrays.asList("Berta", "Ula", "Bob", "John", "Frey", "Tim");
System.out.println("Orginal names: " + names);
List<Strings> subNames = names.subList(2,5);
System.out.println("Sub names: " + names);

Ausgabe:

Orginal names: [Berta, Uls, Bob, John, Frey, Tim]
Sub names: [Bob, John, Frey]

Dabei muss man aber berücksichtigen dass die Subliste aber nur ein Ansichtsausschnitt aus der Orginalliste ist. Alle Änderungen die in der Subliste gemachtwerden wirken sich ebenso auf die orginale Liste aus.

Umwandlung zwischen Listen und Arrays

Das Java Colletions Framework erlaubt es uns sehr einfach Listen in Arrays um zu wandeln oder umgekehrt. Die Arrays.asList(T[]) Methode wandelt eine Array mit dem Datentyp T in eine Laiste mit dem Typ T um.

List<String> names = Arrays.asList("Berta", "Ula", "Bob", "John", "Frey", "Tim");

Für die Umwandlung in die Andere Richtung, also von der Liste zum Array, hällt das Interface List uns die Methode toArray() bereit. Diese Methode gibt ein Array vom Datentyp Object, mit allen Elementen, zurück.

List<String> countrys = new ArrayList<String>();
// add elements to the list
Object[] arrayCountrys = countrys.toArray();

Die Methode toArray(t[] a) gibt ein Array entsprechend des angegebenen Typ T zurück.

String[] words = listWords.toArray(new String[0]);
Integer[] numbers = listNumbers.toArray(new Integer[0]);

Bei beiden Methoden werden kopiene der Elemente erstellt. Was bedeutet das die Elemente im neuen Array verändert werden können ohne das dies einen Effekt auf die Elemente in der Liste hat.

Eine Liste als Stream

Seit Java 8 ist es möglich eine Liste in einen Stream konvertieren, um die Vorteile der Streams-API zu nutzen.

  • List.stream () – Gibt einen sequentiellen Stream zurück.
  • List.parallelStream () – Gibt einen möglicherweise parallelen Stream zurück.

Der folgende Code konvertiert beispielsweise Listennummern in einen Stream und berechnet mithilfe der Stream-API die Summe aller Zahlen.

int sum = numbers.stream().reduce(0, (x, y) -> x + y);

Parallele Verarbeitung von Listen

Standardmäßig sind ArrayList und LinkedList nicht threadsicher. Wenn diese also im parallelen Verarbeitungs-Kontext verwendet werden sollen, muss extern mittels der statischen Methode Collections.synchronizedList() synchronisiert werden. Diese gibt eine threadsichere Liste zurück, die die angegebene Liste umschließt.

List<Object> unsafeList = new ArrayList<Object>();
List<Object> safeList = Collections.synchronizedList(unsafeList);

Um über eine solche threadsichere Liste zu iterieren muss diese manuell synchronisiert werden.

synchronized (safeList) {
  Iterator<Object> it = safeList.iterator();
  while (it.hasNext()) {
    System.out.println(it.next());
  }
}

Basierend auf dem Artikel Java List Collection Tutorial and Examples von Nam Ha Minh.


0 Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.