CodeGym /Corsi /JAVA 25 SELF /LinkedHashSet/LinkedHashMap

LinkedHashSet/LinkedHashMap

JAVA 25 SELF
Livello 28 , Lezione 4
Disponibile

1. Che cosa sono LinkedHashSet e LinkedHashMap?

Nella libreria standard di Java ci sono diverse implementazioni delle collection Set e Map. Le più note — sono HashSet e HashMap. Queste strutture forniscono accesso rapido agli elementi tramite hash, ma non garantiscono alcun ordine nell’iterazione degli elementi. Quando l’ordine di attraversamento è importante — vengono in aiuto LinkedHashSet e LinkedHashMap.

  • LinkedHashSet — è un Set che ricorda l’ordine di inserimento degli elementi.
  • LinkedHashMap — è una Map che ricorda l’ordine di inserimento delle coppie chiave–valore (oppure, a scelta, l’ordine di accesso).

Sono implementati come «normali» HashSet/HashMap, ma con l’aggiunta di una lista doppiamente collegata per mantenere l’ordine degli elementi.

2. Ordine di inserimento e ordine di accesso

Ordine di inserimento

Per impostazione predefinita LinkedHashSet e LinkedHashMap garantiscono che, durante l’iterazione, gli elementi vengano restituiti nell’ordine in cui sono stati inseriti (for-each/iteratore).

Set<String> set = new LinkedHashSet<>();
set.add("A");
set.add("B");
set.add("C");
for (String s : set) {
    System.out.println(s);
}
// Stamperà: 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));
}
// Stamperà: 1 -> one, 2 -> two, 3 -> three

Ordine di accesso (solo per LinkedHashMap)

La LinkedHashMap dispone di una modalità di ordine per ultimo accesso (access order). Se alla creazione della mappa si passa true come terzo parametro del costruttore, a ogni accesso a un elemento (get/put) questo viene spostato alla fine della lista.

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); // Accesso alla chiave 2

for (Integer key : lruMap.keySet()) {
    System.out.println(key);
}
// Stamperà: 1 3 2 (2 — ultimo, perché è stato appena consultato)

Cache LRU tramite removeEldestEntry

La principale «chicca» di LinkedHashMap è la semplice implementazione di una cache LRU (Least Recently Used, «meno recentemente utilizzato»). È sufficiente sovrascrivere il metodo removeEldestEntry(Map.Entry<K,V> eldest): se restituisce true, l’elemento più «vecchio» viene rimosso all’aggiunta di un nuovo elemento.

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

    public LRUCache(int maxSize) {
        super(maxSize, 0.75f, true); // true — ordine per accesso
        this.maxSize = maxSize;
    }

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

// Utilizzo:
LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "one");
cache.put(2, "two");
cache.put(3, "three");
cache.get(1); // Rende 1 il più "recente"
cache.put(4, "four"); // Verrà rimosso il 2 (il più vecchio)
System.out.println(cache.keySet()); // [3, 1, 4]

3. Costo in memoria e prestazioni: LinkedHashSet/LinkedHashMap vs HashSet/HashMap

Come funziona

All’interno — quasi come i normali HashSet/HashMap, ma ogni elemento è memorizzato anche in una lista doppiamente collegata. Questa struttura aggiuntiva «ricorda» l’ordine di inserimento/accesso.

Costo in memoria

A causa dei riferimenti prev/next su ogni nodo è necessaria più memoria. Per milioni di elementi questo è sensibile; per i casi tipici — è un costo ragionevole in cambio di un ordine deterministico.

Costo in velocità

Le operazioni di base di inserimento/ricerca/rimozione restano ammortizzate O(1). L’iterazione è leggermente più lenta a causa del passaggio attraverso la lista, ma nella maggior parte dei casi la differenza è minima. E lo scenario «rimuovi il più vecchio» (LRU) è molto efficiente, perché l’elemento «più anziano» è sempre in testa alla lista.

In sintesi: se l’ordine è importante — scegli LinkedHashSet/LinkedHashMap. Se occorre risparmiare memoria e l’ordine non serve — basta HashSet/HashMap.

4. Caching, output deterministico, test stabili

Caching (LRU)

LinkedHashMap — una base ideale per cache con limite di dimensione e rimozione automatica degli elementi «vecchi». Usata in librerie/framework, nella gestione di file, immagini, risultati di calcolo.

Output deterministico

Per report, export e serializzazione, dove è cruciale una rappresentazione dei dati prevedibile, usa LinkedHashSet/LinkedHashMap. È particolarmente importante per i test — l’ordine non «balla» da un’esecuzione all’altra.

Test stabili

Nei test unitari spesso si confrontano le collection attese con quelle ottenute. Se l’ordine non è garantito, i test possono «flappare». Con le implementazioni «linked» — il determinismo garantisce stabilità.

Esempio: confronto tra HashMap e 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());   // L’ordine può essere qualsiasi!
System.out.println(linkedMap.keySet()); // Sempre 1, 2, 3, 4, 5

5. LinkedList vs ArrayDeque per le code

LinkedList

  • Implementa le interfacce List, Deque, Queue.
  • Lista doppiamente collegata: inserimenti/rimozioni veloci all’inizio e alla fine.
  • Può essere usata come coda (FIFO), stack (LIFO), coda doppia.

ArrayDeque

  • Implementa Deque, Queue (ma non List).
  • Basata su array, si espande automaticamente.
  • Spesso più veloce di LinkedList per operazioni di coda/stack; overhead minore.
  • Non supporta l’elemento null.

Quando usare cosa?

  • Code e stack — quasi sempre ArrayDeque.
  • Molti inserimenti/rimozioni in mezzo e serve anche una lista — LinkedList.
  • Set/Map con ordineLinkedHashSet/LinkedHashMap.

Esempio: coda di task

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

Esempio: stack

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

Conclusione:
— Per code e stack — ArrayDeque.
— Per liste con inserimenti frequenti in mezzo — LinkedList.
— Per Set/Map con ordine — LinkedHashSet/LinkedHashMap.

6. Errori tipici nell’uso di LinkedHashSet/LinkedHashMap

Errore n. 1: aspettarsi che HashSet/HashMap preservino l’ordine.
HashSet/HashMap non garantiscono alcun ordine. Se è importante — usa LinkedHashSet/LinkedHashMap.

Errore n. 2: LinkedHashMap per cache senza override di removeEldestEntry.
Per LRU bisogna sovrascrivere removeEldestEntry, altrimenti la mappa crescerà senza limiti.

Errore n. 3: usare LinkedList per code/stack senza necessità.
Nella maggior parte dei casi ArrayDeque è più veloce ed economica.

Errore n. 4: aspettarsi l’ordinamento da LinkedHashMap.
LinkedHashMap preserva l’ordine di inserimento/accesso, ma non ordina per chiave. Per l’ordinamento — TreeMap.

Errore n. 5: inserire null in ArrayDeque.
ArrayDeque non supporta elementi null — verrà lanciata NullPointerException.

Errore n. 6: confrontare le collection senza considerare l’ordine.
Confrontando LinkedHashSet/LinkedHashMap con i normali HashSet/HashMap, tieni conto delle differenze nell’ordine degli elementi.

1
Compito
JAVA 25 SELF, livello 28, lezione 4
Bloccato
Creazione della lista "Prodotti visualizzati di recente" per un negozio online 🛍️
Creazione della lista "Prodotti visualizzati di recente" per un negozio online 🛍️
1
Compito
JAVA 25 SELF, livello 28, lezione 4
Bloccato
Sviluppo di una cache intelligente per asset di gioco 🚀
Sviluppo di una cache intelligente per asset di gioco 🚀
1
Sondaggio/quiz
Lavorare con le collezioni, livello 28, lezione 4
Non disponibile
Lavorare con le collezioni
Lavorare con le collezioni
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION