CodeGym /Kurse /JAVA 25 SELF /LinkedHashSet/LinkedHashMap

LinkedHashSet/LinkedHashMap

JAVA 25 SELF
Level 28 , Lektion 4
Verfügbar

1. Was sind LinkedHashSet und LinkedHashMap?

In der Java-Standardbibliothek gibt es mehrere Implementierungen von Set- und Map-Collections. Die bekanntesten sind HashSet und HashMap. Diese Strukturen bieten schnellen Zugriff per Hash, garantieren aber keine bestimmte Reihenfolge beim Iterieren. Wenn die Iterationsreihenfolge wichtig ist, helfen LinkedHashSet und LinkedHashMap.

  • LinkedHashSet ist ein Set, das sich die Einfügereihenfolge der Elemente merkt.
  • LinkedHashMap ist eine Map, die sich die Einfügereihenfolge der Schlüssel–Wert-Paare merkt (oder optional die Zugriffsreihenfolge).

Sie sind wie „gewöhnliche“ HashSet/HashMap implementiert, werden jedoch durch eine doppelt verkettete Liste ergänzt, um die Reihenfolge der Elemente zu speichern.

2. Einfüge- und Zugriffsreihenfolge

Einfügereihenfolge

Standardmäßig garantieren LinkedHashSet und LinkedHashMap, dass beim Iterieren die Elemente in der Reihenfolge zurückgegeben werden, in der sie hinzugefügt wurden (for-each/Iterator).

Set<String> set = new LinkedHashSet<>();
set.add("A");
set.add("B");
set.add("C");
for (String s : set) {
    System.out.println(s);
}
// Ausgabe: A B C
Map<Integer, String> map = new LinkedHashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for (Integer key : map.keySet()) {
    System.out.println(key + " -> " + map.get(key));
}
// Ausgabe: 1 -> one, 2 -> two, 3 -> three

Zugriffsreihenfolge (nur für LinkedHashMap)

LinkedHashMap besitzt einen Modus der letzten Zugriffe (access order). Wenn man beim Erzeugen der Map true als dritten Konstruktor-Parameter übergibt, wird jedes Element bei einem Zugriff (get/put) ans Ende der Liste verschoben.

Map<Integer, String> lruMap = new LinkedHashMap<>(16, 0.75f, true);
lruMap.put(1, "one");
lruMap.put(2, "two");
lruMap.put(3, "three");

lruMap.get(2); // Zugriff auf Schlüssel 2

for (Integer key : lruMap.keySet()) {
    System.out.println(key);
}
// Ausgabe: 1 3 2 (2 ist der letzte, weil darauf zugegriffen wurde)

LRU-Cache über removeEldestEntry

Der wichtigste „Clou“ von LinkedHashMap ist die einfache Implementierung eines LRU-Caches (Least Recently Used, „am längsten nicht verwendet“). Es genügt, die Methode removeEldestEntry(Map.Entry<K,V> eldest) zu überschreiben: Gibt sie true zurück, wird beim Hinzufügen eines neuen Elements das „älteste“ Element entfernt.

class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxSize;

    public LRUCache(int maxSize) {
        super(maxSize, 0.75f, true); // true: Zugriffsreihenfolge
        this.maxSize = maxSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxSize;
    }
}

// Verwendung:
LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "one");
cache.put(2, "two");
cache.put(3, "three");
cache.get(1); // Macht 1 zum "frischesten"
cache.put(4, "four"); // 2 wird entfernt (der älteste)
System.out.println(cache.keySet()); // [3, 1, 4]

3. Speicher- und Laufzeitkosten: LinkedHashSet/LinkedHashMap vs HashSet/HashMap

Wie es aufgebaut ist

Intern – fast wie bei „normalen“ HashSet/HashMap –, aber jedes Element wird zusätzlich in einer doppelt verketteten Liste gespeichert. Diese zusätzliche Struktur „merkt sich“ die Einfüge- bzw. Zugriffsreihenfolge.

Speicherbedarf

Wegen der Referenzen prev/next an jedem Knoten wird mehr Speicher benötigt. Bei Millionen von Elementen ist das spürbar; für typische Aufgaben ist es ein gerechtfertigter Preis für eine deterministische Reihenfolge.

Laufzeitkosten

Die Basisoperationen Einfügen/Suchen/Löschen bleiben amortisiert O(1). Das Iterieren ist etwas langsamer wegen der Listendurchläufe, aber meist ist der Unterschied minimal. Das Szenario „den ältesten löschen“ (LRU) ist sehr effizient, da das „älteste“ Element immer am Kopf der Liste steht.

Fazit: Wenn die Reihenfolge wichtig ist – wählen Sie LinkedHashSet/LinkedHashMap. Wenn Speicher gespart werden muss und die Reihenfolge egal ist – genügt HashSet/HashMap.

4. Caching, deterministische Ausgabe, stabile Tests

Caching (LRU)

LinkedHashMap ist eine ideale Grundlage für Caches mit Größenlimit und automatischem Entfernen „alter“ Einträge. Sie wird in Bibliotheken/Frameworks, bei der Arbeit mit Dateien, Bildern und Rechenergebnissen eingesetzt.

Deterministische Ausgabe

Für Berichte, Export, Serialisierung – überall, wo eine vorhersagbare Darstellung der Daten kritisch ist – verwenden Sie LinkedHashSet/LinkedHashMap. Das ist besonders für Tests wichtig – die Reihenfolge „springt“ nicht von Lauf zu Lauf.

Stabile Tests

In Unit-Tests vergleicht man häufig erwartete Collections mit erhaltenen. Ist die Reihenfolge nicht garantiert, können Tests „flappen“. Mit den „linked“-Implementierungen sorgt die Determiniertheit für Stabilität.

Beispiel: Vergleich HashMap und LinkedHashMap

Map<Integer, String> hashMap = new HashMap<>();
Map<Integer, String> linkedMap = new LinkedHashMap<>();

for (int i = 1; i <= 5; i++) {
    hashMap.put(i, "val" + i);
    linkedMap.put(i, "val" + i);
}

System.out.println(hashMap.keySet());   // Reihenfolge kann beliebig sein!
System.out.println(linkedMap.keySet()); // Immer 1, 2, 3, 4, 5

5. LinkedList vs ArrayDeque für Queues

LinkedList

  • Implementiert die Interfaces List, Deque, Queue.
  • Doppelt verkettete Liste: schnelle Einfügungen/Löschungen am Anfang und am Ende.
  • Kann als Queue (FIFO), Stack (LIFO) und doppelt-gerichtete Queue verwendet werden.

ArrayDeque

  • Implementiert Deque, Queue (aber nicht List).
  • Basiert auf einem Array, wächst automatisch.
  • Oft schneller als LinkedList für Queue-/Stack-Operationen; geringerer Overhead.
  • Unterstützt kein Element null.

Wann was verwenden?

  • Queues und Stacks – fast immer ArrayDeque.
  • Viele Einfügungen/Löschungen in der Mitte und zusätzlich eine Liste nötig – LinkedList.
  • Set/Map mit ReihenfolgeLinkedHashSet/LinkedHashMap.

Beispiel: Aufgaben-Queue

Queue<String> queue = new ArrayDeque<>();
queue.add("task1");
queue.add("task2");
System.out.println(queue.poll()); // task1

Beispiel: Stack

Deque<String> stack = new ArrayDeque<>();
stack.push("first");
stack.push("second");
System.out.println(stack.pop()); // second

Fazit:
– Für Queues und Stacks – ArrayDeque.
– Für Listen mit häufigen Einfügungen in der Mitte – LinkedList.
– Für Set/Map mit Reihenfolge – LinkedHashSet/LinkedHashMap.

6. Häufige Fehler bei der Arbeit mit LinkedHashSet/LinkedHashMap

Fehler Nr. 1: Erwartung, dass HashSet/HashMap die Reihenfolge bewahren.
HashSet/HashMap garantieren keine Reihenfolge. Wenn sie wichtig ist – verwenden Sie LinkedHashSet/LinkedHashMap.

Fehler Nr. 2: LinkedHashMap als Cache ohne Überschreiben von removeEldestEntry.
Für LRU muss removeEldestEntry überschrieben werden, sonst wächst die Map unbegrenzt.

Fehler Nr. 3: Verwendung von LinkedList für Queues/Stacks ohne Notwendigkeit.
In den meisten Fällen ist ArrayDeque schneller und sparsamer.

Fehler Nr. 4: Von LinkedHashMap eine Sortierung erwarten.
LinkedHashMap bewahrt Einfüge-/Zugriffsreihenfolge, sortiert aber nicht nach Schlüssel. Zum Sortieren – TreeMap.

Fehler Nr. 5: Hinzufügen von null zu ArrayDeque.
ArrayDeque unterstützt keine Elemente null – es kommt zu NullPointerException.

Fehler Nr. 6: Vergleich von Collections ohne Berücksichtigung der Reihenfolge.
Beim Vergleich von LinkedHashSet/LinkedHashMap mit gewöhnlichen HashSet/HashMap berücksichtigen Sie die unterschiedlichen Elementreihenfolgen.

1
Umfrage/Quiz
Arbeiten mit Collections, Level 28, Lektion 4
Nicht verfügbar
Arbeiten mit Collections
Arbeiten mit Collections
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION