jario_
Zur Übersicht

Kapitel 8

Listen und Maps

Ein Array hat eine feste Größe. Oft weißt du aber vorher nicht, wie viele Elemente es werden: Einträge im Warenkorb, Songs in einer Playlist. Java bringt dafür fertige Sammlungen mit, die Collections. Die drei wichtigsten sind Liste, Set und Map. Alle liegen in java.util und brauchen einen import.

ArrayList

Eine ArrayList ist wie ein Array, das mitwächst. In spitzen Klammern steht, was hineinkommt:

List<String> names = new ArrayList<>();

Links steht der allgemeine Typ List, rechts die konkrete ArrayList. Die leeren Klammern <> (der „Diamant“) übernehmen den Typ von links.

  • add(x) hängt hinten an, get(i) liest Position i (ab 0), set(i, x) ersetzt
  • size() ist die Anzahl, isEmpty() prüft auf leer
  • remove(i) entfernt Position i, alles dahinter rutscht nach vorn
  • contains(x) sucht ein Element

In spitze Klammern gehören nur Klassen. Für Zahlen schreibst du Integer statt int und Double statt double. Beim Hinzufügen und Lesen wandelt Java automatisch um. Eine Liste kannst du direkt ausgeben, anders als ein Array. List.of("a", "b") erzeugt schnell eine feste Liste, die sich danach nicht mehr ändern lässt.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Ada");
        names.add("Bob");
        names.add("Cleo");
        System.out.println(names);
        System.out.println("Size: " + names.size());
        System.out.println("Index 1: " + names.get(1));

        names.remove(0);
        names.add("Dan");
        System.out.println(names);
        System.out.println(names.contains("Ada"));

        List<Integer> squares = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            squares.add(i * i);
        }
        int sum = 0;
        for (int n : squares) {
            sum += n;
        }
        System.out.println(squares + " sum " + sum);
    }
}

Set: jedes Element nur einmal

Ein Set enthält jedes Element höchstens einmal. add gibt false zurück, wenn das Element schon drin ist, und lässt das Set unverändert. Damit entfernst du Doppelte oder zählst verschiedene Werte.

  • HashSet: schnell, aber ohne feste Reihenfolge
  • LinkedHashSet: behält die Reihenfolge, in der die Elemente dazukamen

Ein Set hat kein get(i). Brauchst du wieder eine Liste, übergibst du das Set dem Konstruktor: new ArrayList<>(set).

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        List<String> signUps = List.of("Mia", "Leo", "Mia", "Ana", "Leo");
        Set<String> guests = new LinkedHashSet<>();
        for (String name : signUps) {
            boolean added = guests.add(name);
            if (!added) {
                System.out.println(name + " is already on the list");
            }
        }
        System.out.println(guests);
        System.out.println(guests.size() + " guests");
        System.out.println(guests.contains("Ana"));

        List<String> list = new ArrayList<>(guests);
        System.out.println("Last: " + list.get(list.size() - 1));
    }
}

HashMap: Schlüssel und Wert

Eine Map speichert Paare aus Schlüssel und Wert, etwa Name → Telefonnummer. Map<String, String> nennt beide Typen.

  • put(key, value) speichert; gibt es den Schlüssel schon, wird der alte Wert ersetzt
  • get(key) liefert den Wert, oder null, wenn der Schlüssel fehlt
  • getOrDefault(key, fallback) liefert stattdessen den Ersatzwert
  • containsKey(key), size(), keySet() für alle Schlüssel

Zum Zählen nimmst du die bisherige Anzahl (oder 0) und speicherst sie plus eins, wie im Beispiel. Werte dürfen selbst Listen sein: Map<String, List<Integer>> speichert zu jedem Namen eine Liste von Noten.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, String> phone = new HashMap<>();
        phone.put("Ada", "0151 123");
        phone.put("Bob", "0160 456");
        phone.put("Ada", "0170 789");
        System.out.println(phone.get("Ada"));
        System.out.println(phone.get("Eve"));
        System.out.println(phone.getOrDefault("Eve", "unknown"));
        System.out.println(phone.containsKey("Bob"));
        System.out.println(phone.size() + " entries");

        String text = "the cat and the hat and the bat";
        Map<String, Integer> counts = new HashMap<>();
        for (String word : text.split(" ")) {
            counts.put(word, counts.getOrDefault(word, 0) + 1);
        }
        System.out.println("the: " + counts.get("the"));
        System.out.println("and: " + counts.get("and"));
        System.out.println("cat: " + counts.get("cat"));
    }
}

Typische Fehler

  • List<int>

    List<int> meldet der Compiler mit unexpected type. In spitzen Klammern stehen nur Klassen: List<Integer>, Map<String, Double>.

  • remove bei List<Integer>

    remove gibt es zweimal: mit einem int entfernt es eine Position, mit einem Objekt das Element. Bei einer Liste von Zahlen ist das eine Falle:

    List<Integer> numbers = new ArrayList<>(List.of(10, 20, 30));
    numbers.remove(1);                  // removes position 1: [10, 30]
    numbers.remove(Integer.valueOf(10)); // removes the value 10: [30]
  • Liste in for-each ändern

    Entfernst oder ergänzt du Elemente, während eine for-each-Schleife über dieselbe Liste läuft, bricht das Programm mit ConcurrentModificationException ab. Nimm eine normale for-Schleife mit Index oder sammle in einer neuen Liste.

  • Fehlender Schlüssel ergibt null

    int n = counts.get("dog"); bricht mit NullPointerException ab, wenn es den Schlüssel nicht gibt, weil null keine Zahl ist. getOrDefault(key, 0) oder vorher containsKey prüfen.

  • Reihenfolge bei HashMap und HashSet

    HashMap und HashSet merken sich nicht, in welcher Reihenfolge du etwas eingefügt hast. Verlass dich bei der Ausgabe nicht darauf. Brauchst du die Reihenfolge, nimm LinkedHashSet bzw. LinkedHashMap.