Java Collection Framework
ArrayList
Willemers Informatik-Ecke
Java Collection Framework Java LinkedList

Arrays sind Bestandteile der Sprache Java, sind aber wenig flexibel, was die Länge angeht. Einmal angelegt, lassen sie sich nicht mehr vergrößern. Sollte zu den 100 Elementen noch eines hinzukommen, muss man ein längeres Array erzeugen und die Elemente des bisherigen umkopieren. So etwas will man sich nicht antun.

Eine ArrayList kann man sich vorstellen wie Bauklötze, die direkt nebeneinander stehen. Man findet über die Nummer einen Bauklotz sehr schnell, weil man nur die Breite eines Bausteins mit der Position multiplizieren muss und schon findet man auch bei sehr vielen Bauklötzen einen bestimmten Bauklotz. Immer vorausgesetzt, man hat einen beliebig langen Zollstock.

ArrayList ist eine Implementierung des Interfaces List, das wiederum eine Erweiterung des Interfaces Collection ist.

Anlegen eines Arrays

Als erstes Beispiel soll ein Array von Strings angelegt werden. Das Gegenstück der ArrayList muss sich nicht auf die acht Elemente festlegen. Der String als Datentyp wird per Generics festgelegt.

Array
String[] strings = new String[8];
ArrayList
ArrayList<String> diaStrgs = new ArrayList<>();

Das Array ist nun bereit, acht Strings in beliebiger Reihenfolge aufzunehmen. Die ArrayList muss schrittweise durch den Aufruf von add aufgefüllt werden.

Array
for (int i=0; i<8; i++) {
    strings[i] = "" + (char)('A'+i) + (i+1)); 
}
ArrayList
for (int i=0; i<8; i++) {
    diaStrgs.add("" + (char)('A'+i) + (i+1));
}

Die Änderung von Werten erfolgt beim Array, indem das Array-Element als LValue links neben den Zuweisungsoperator gestellt wird. Bei der ArrayList wird die Methode set verwendet. Der erste Parameter ist der Index, der zweite das neue Element.

Array
strings[i] = "4D";
ArrayList
diaStrgs.set(i, "4D");

Zum Auslesen eines Elements wird beim Array das Element auf die rechte Seite des Zuweisungsoperators gestellt. Bei ArrayList wird die Methode get aufgerufen und der Index übergeben.

Array
String str = strings[i];
ArrayList
String str = diaStrgs.get(i); 

Die Länge eines Arrays finden Sie in der Variable length. Ihr Inhalt entspricht dem Wert, der bei der Erzeugung des Arrays verwendet wurde. ArrayList kennt für diese Zwecke die Methode size. Sie gibt immer den aktuellen Füllstand wieder, also wie viele Elemente per add angehängt wurden.

Array
for (int i=0; i<strings.length; i++) {
ArrayList
for (int i=0; i<diaStrgs.size(); i++) {

Beispiel

Die Strings, die in der ArrayList gespeichert werden sollen, stellen die Koordinaten der Diagonale eines Schachbretts dar (A1, B2, ...) dar. Das vierte Element wird allerdings anschließend auf 4D gesetzt. Zu guter Letzt wird der Inhalt der ArrayList ausgegeben, je durch ein Leerzeichen getrennt.
import java.util.ArrayList;

public class ArrayListTest {

    public static void main(String[] args) {
        ArrayList<String> diaStrgs = new ArrayList<>();
        for (int i=0; i<8; i++) {
            diaStrgs.add("" + (char)('A'+i) + (i+1));
        }
        diaStrgs.set(3, "4D");
        for (int i=0; i<8; i++) {
            System.out.print(diaStrgs.get(i) + ' ');
        }
    }
}
Das Programm gibt die folgende Zeile aus:
A1 B2 C3 4D E5 F6 G7 H8 

Kapazitäten

Ja, auch eine ArrayList hat Grenzen. Allerdings sind sie kaum spürbar. Und was schöner ist: Der Programmierer muss sich nicht darum kümmern. Diese Grenze nennt sich Kapazität.

Standardmäßig hat eine ArrayList eine Kapazität von zehn Elementen. Wird mehr Platz benötigt, legt die ArrayList einen größeren Speicher an und kopiert die Elemente um. Das tut die ArrayList dankenswerterweise von sich aus.

Ist von vornherein absehbar, dass die ArrayList sehr viel größer wird, dann ist es sinnvoll, die Kapazität bei Aufruf des Konstruktors zu übergeben.

ArrayList namen = new ArrayList(1000);
Sollte sich erst später herausstellen, dass eine höhere Kapazität erforderlich ist, kann mit der Methode esureCapacity nachgeholfen werden. Ihr Parameter legt die Kapazität fest.
namen.ensureCapacity(2000);
Sollte sich irgendwann herausstellen, dass eine ArrayList eine viel zu große Kapazität besitzt, kann diese duch den Aufruf von trimToSize auf die aktuelle Größe reduziert werden.
namen.trimToSize()

Zugriff auf die Elemente über einen Iterator

Der Zugriff über den Index ist die Spezialität der ArrayList. Wie alle anderen Collections kann sie allerdings auch einen Iterator liefern, über den man, wie per Zeigestock auf die Elemente zugreifen kann.

Ein Iterator wird von der ArrayList durch Aufruf der Methode iterator geliefert. Der neu gewonnene Iterator steht zunächst vor dem ersten Element. Der Iterator kann nun über seine Methode hasNext feststellen, ob seine Collection noch Elemente enthält und über seine Methode next kann er ihn liefern und zum nächsten Element fortschreiten. Eine Schleife zum Auslesen sieht also so aus:

Iterator<String> iter = diaStrgs.iterator();
while (iter.hasNext()) {
    String element = iter.next();
    System.out.print(element + ' ');
}

Landkreise in der ArrayList

In einem Beispiel sollen Daten in einer ArrayList verwaltet werden. Bevor wir aber auf die Liste schauen, sehen wir uns die Elemente an. Unsere Beispieldaten sind Landkreise. Die haben eine Bezeichnung und eine Einwohnerzahl. Die Klasse ist sehr übersichtlich.
public class Kreis {
    private String wo;
    private long einwohner;

    Kreis(String w, long e) {
        wo = w; einwohner = e;
    }

    String getWo() {
        return wo;
    }

    long getEinwohner() {
        return einwohner;
    }
}
Das folgende Hauptprogramm legt eine ArrayList von Kreisen an und füllt sie ein paar Elementen. Dazu wird recht unspektakulär die Collection-Methode add verwendet, die die Daten am Ende anhängt.

In dem Programm werden die Elemente einmal per Iterator und einmal per Index durchlaufen. Dazwischen werden zwei Kreise getauscht und ein Kreis entfernt.

import java.util.ArrayList;
import java.util.Iterator;

public class TestArrayListKreise {

    public static void main(String args[]) {
        ArrayList<Kreis> kreise = new ArrayList<Kreis>();
        kreise.add(new Kreis("Schleswig-Flensburg",197903));
            // Mit Collection-Methode add Elemente anhängen
        kreise.add(new Kreis("Hochtaunuskreis",227425));
        kreise.add(new Kreis("Mühlheim",167344));
        kreise.add(new Kreis("Pinneberg",303481));
        kreise.add(new Kreis("Recklinghausen",628817));
        System.out.println("--- Liste per Iterator ausgeben");
        Iterator<Kreis> iterator = kreise.iterator();
        while (iterator.hasNext()) {
            Kreis k = iterator.next();
            System.out.print("" + k.getWo());
            System.out.print(" (" + k.getEinwohner());
            System.out.println(" Einwohner)");
        }
        // tausche Pos 1 und 2 per Index mit set und get
        Kreis help = kreise.get(1);
        kreise.set(1, kreise.get(2));
        kreise.set(2, help);
        kreise.remove(3); // 4tes (Pos 3!) Element wegwerfen
        System.out.println("--- Liste per for nach Tausch und Löschung");
        for (int i=0; i<kreise.size(); i++) {
            System.out.print("" + i + ": " + kreise.get(i).getWo());
            System.out.print(" (" + kreise.get(i).getEinwohner());
            System.out.println(" Einwohner)");
        }
    }
}
Wie beim Array beginnen die Indizes der ArrayList bei 0. Das vierte Element steht also an Position 3.

Der Tausch zwischen den beiden Elementen über ihren Index erfolgt bei einer ArrayList schneller als bei einer LinkedList. Dagegen ist das remove nicht ganz so günstig, weil die Elemente aufgeschoben werden müssen. Immerhin ist der remove günstiger als beim Array, weil sich der Programmierer nicht selbst um die Organisation der Arraydaten kümmern muss.

Statt der for-Schleife, die über den Index geht, könnte auch die Spezial-for-Schleife für Arrays verwendet werden. Das Objekt der Klasse Kreis wird im Schleifenkopf als Element der ArrayList kreise deklariert. Daraufhin kann im Schleifenkörper auf dieses Objekt zugegriffen werden und die Schleife läuft über alle Elemente der Liste.

for (Kreis k : kreise) {
    System.out.print("" + k.getWo());
    System.out.print(" (" + k.getEinwohner());
    System.out.println(" Einwohner)");
}

Noch ein flexibles Array: Vector

Bevor die ArrayList im Zusammenhang mit dem Java Collection Framework entstand, gab es bereits die Klasse Vector, die das gleiche Ziel verfolgte, nämlich ein dynamisches Array zu implementieren.

Damit der Vector nicht so einsam ist, wurde er nachträglich auf die Basis des Interfaces List gestellt, so dass man eine ArrayList einfach gegen einen Vector im Listing ersetzen könnte. Einen feinen Unterschied gibt es. Die Methoden des Vectors sind synchronisiert. Das bedeutet, dass sie in Umgebungen mit mehreren Threads ununterbrechbar laufen. Das kann natürlich auch Konsequenzen in ihrer Laufzeit haben. Da die Definition atomarer Abläufe normalerweise sowieso von der Anwendung gemacht werden muss, bringt ein Vector in der Praxis keine Vorteile.

Den Vector sollte man schon deshalb kennen, weil er in vielen älteren Sourcen vorkommt. In diesen werden dann auch manchmal Methoden verwendet, die im Interface von List nicht vorkommen.

So wird manchmal setElementAt(Element, index) anstelle von set(index,Element) verwendet. ABgesehen vom Namen muss man hier beachten, dass die Parameter vertauscht sind. Zum Löschen wird hin und wieder removeElementAt(index) statt remove(index) eingesetzt. Da Vector inzwischen das Interface List implementiert, kann man natürlich set und remove verwenden.

Ein sehr hübsches Beispiel für den Umgang mit Vektoren ist ein MauMau-Kartenspiel unter der folgenden URL:

http://www.must.de/default.html?Java-Vector-Beispiel.html

Der Computer spielt nur gegen sich selbst und gewinnt dadurch natürlich immer.


Java Collection Framework Java LinkedList