ArrayList
Kurz: Die gebräuchlichste List-Implementierung in Java — intern ein dynamisch wachsendes Array, das bei Bedarf automatisch vergrößert wird.
Genauer: Zugriff per Index (get(i)) ist sehr schnell (konstante Zeit), Einfügen/Entfernen in der Mitte dagegen langsam, da nachfolgende Elemente verschoben werden müssen. Im Gegensatz zu einem klassischen Array muss die Größe nicht vorher feststehen.
List<String> namen = new ArrayList<>();
namen.add("Anna");
namen.get(0);Im Detail
Intern hält eine ArrayList ein normales Array als Backing-Array. Ist dieses voll und ein weiteres Element soll hinzugefügt werden, erzeugt Java automatisch ein neues, größeres Array (typischerweise das 1,5-fache der alten Größe) und kopiert alle Elemente hinüber — für den Aufrufer unsichtbar, aber nicht kostenlos. Wer die ungefähre Endgröße schon kennt, spart sich wiederholtes Vergrößern mit dem Konstruktor new ArrayList<>(erwarteteGroesse).
List<String> namen = new ArrayList<>();
namen.add("Anna");
namen.add("Ben");
namen.add(0, "Erster"); // Einfügen an Position 0 - verschiebt alles danach
namen.get(0); // "Erster" - O(1), sehr schnell
namen.remove(0); // entfernt "Erster" - O(n), alle folgenden rücken auf
namen.contains("Ben"); // true - O(n), durchsucht linear
for (String name : namen) { // For-Each nutzt intern einen Iterator
System.out.println(name);
}Wichtige Fallstricke: ArrayList ist NICHT threadsicher (mehrere Threads dürfen sie nicht gleichzeitig verändern, sonst drohen ConcurrentModificationException oder Datenkorruption — dafür gibt es Collections.synchronizedList() oder CopyOnWriteArrayList). Und: Elemente während einer laufenden For-Each-Schleife direkt über liste.remove() zu entfernen wirft eine ConcurrentModificationException — dafür braucht es stattdessen iterator.remove() über einen expliziten Iterator.
Häufige Operationen im Überblick
List<Integer> zahlen = new ArrayList<>(List.of(5, 3, 8));
zahlen.set(0, 99); // ersetzt Element an Index 0
zahlen.indexOf(8); // 2 - erste Fundstelle, -1 wenn nicht vorhanden
zahlen.isEmpty(); // false
zahlen.size(); // 3
zahlen.clear(); // leert die Liste komplett
Collections.sort(zahlen); // sortiert in-place (siehe auch Sorting)ArrayList vs. LinkedList
Die zweite gängige List-Implementierung ist LinkedList, intern eine doppelt verkettete Liste. Bei ArrayList ist wahlfreier Zugriff (get(i)) sehr schnell (O(1)), Einfügen am Anfang/in der Mitte dagegen langsam (O(n), da Elemente verschoben werden müssen). Bei LinkedList ist es umgekehrt: Einfügen am Anfang/Ende ist O(1) (nur Zeiger umbiegen), aber get(i) muss die Kette von vorne durchlaufen (O(n)). In der Praxis ist ArrayList fast immer die bessere Standardwahl — moderne CPUs bevorzugen den zusammenhängenden Speicherbereich eines Arrays (Cache-Lokalität), LinkedList lohnt sich nur bei sehr häufigem Einfügen/Entfernen an beiden Enden ohne wahlfreien Zugriff.
Generics und Autoboxing
ArrayList<Integer> speichert intern keine primitiven int-Werte, sondern Integer-Objekte — jedes Element wird beim Einfügen automatisch “geboxt” (int → Integer) und beim Auslesen wieder “unboxt”. Das kostet bei sehr großen Listen mit primitiven Zahlen spürbar Speicher und Performance im Vergleich zu einem rohen int[]-Array — ein Grund, warum performancekritischer Code manchmal bewusst auf Arrays statt Collections zurückgreift.
Siehe auch: List, LinkedList, Arrays, Iterator, Generics