CodeGym /Corsi /JAVA 25 SELF /Nuove collezioni: SequencedCollection, SequencedSet, Sequ...

Nuove collezioni: SequencedCollection, SequencedSet, SequencedMap

JAVA 25 SELF
Livello 34, Lezione 4
Disponibile

1. Il problema dell'ordine nelle collezioni classiche

In Java ci sono sempre state collezioni che garantiscono l'ordine degli elementi (ad esempio, ArrayList, LinkedList, LinkedHashSet, LinkedHashMap) e altre in cui l'ordine non è garantito (ad esempio, HashSet, HashMap). Tuttavia, tutte queste collezioni avevano una mancanza comune: benché alcune di esse mantengano gli elementi in un certo ordine, le interfacce standard (List, Set, Map) non fornivano metodi universali per accedere al primo e all'ultimo elemento o per invertire l'ordine.

Per esempio, se avete un List<String>, potete ottenere il primo elemento con list.get(0), ma per Set o Map questo trucco non funziona — bisogna usare gli iteratori o scrivere codice superfluo. Ammettiamolo, non è molto comodo e non aiuta la leggibilità.

È come avere un armadio con cassetti, ma per prendere il primo o l'ultimo cassetto dovreste ogni volta contarli tutti a mano! Sarebbe bello avere delle maniglie «primo» e «ultimo», giusto?

La nascita di SequencedCollection e delle interfacce correlate

In Java 21 sono state introdotte nuove interfacce di collezioni:

  • SequencedCollection<E>
  • SequencedSet<E>
  • SequencedMap<K, V>

Queste interfacce estendono le collezioni standard e introducono un approccio unificato alla gestione dell'ordine degli elementi. Ora si può scrivere codice universale per qualsiasi collezione in cui l'ordine sia importante, senza preoccuparsi della specifica implementazione.

Che cosa sono?

  • SequencedCollection è una collezione in cui gli elementi hanno un ordine definito, e potete facilmente ottenere il primo e l'ultimo elemento, oltre a invertire l'ordine.
  • SequencedSet — lo stesso, ma per gli insiemi (elementi unici).
  • SequencedMap — lo stesso, ma per le mappe (chiave-valore).

Quali collezioni ora li implementano?

In Java 21 le seguenti collezioni standard implementano le nuove interfacce:

  • ArrayList, LinkedList → SequencedCollection
  • LinkedHashSet, TreeSet → SequencedSet
  • LinkedHashMap, TreeMap → SequencedMap

Ciò significa che, se già usate queste collezioni, ottenete automaticamente le nuove funzionalità!

2. Metodi principali di SequencedCollection, SequencedSet, SequencedMap

Metodi SequencedCollection

E getFirst();      // Ottenere il primo elemento
E getLast();       // Ottenere l'ultimo elemento
SequencedCollection<E> reversed(); // Ottenere la collezione in ordine inverso

Metodi SequencedSet

Gli stessi metodi di SequencedCollection, più tutto ciò che fa Set.

Metodi SequencedMap

Map.Entry<K, V> firstEntry();    // Primo elemento (chiave-valore)
Map.Entry<K, V> lastEntry();     // Ultimo elemento (chiave-valore)
SequencedMap<K, V> reversed();   // Map in ordine inverso

3. Esempi di utilizzo delle nuove interfacce

Esempio 1: ottenere il primo e l'ultimo elemento

import java.util.*;

public class SequencedDemo {
    public static void main(String[] args) {
        SequencedCollection<String> sc = new ArrayList<>();
        sc.add("Java");
        sc.add("Python");
        sc.add("Kotlin");

        // Ottenere il primo e l'ultimo elemento
        String first = sc.getFirst(); // "Java"
        String last = sc.getLast();   // "Kotlin"

        System.out.println("Primo: " + first);
        System.out.println("Ultimo: " + last);
    }
}

Output:

Primo: Java
Ultimo: Kotlin

Esempio 2: inversione della collezione

SequencedCollection<String> sc = new LinkedList<>();
sc.add("A");
sc.add("B");
sc.add("C");

SequencedCollection<String> reversed = sc.reversed();
System.out.println(reversed); // [C, B, A]

Nota: reversed() restituisce una vista della collezione in ordine inverso. Se modificate la collezione originale, cambierà anche la vista reversed!

Esempio 3: lavorare con SequencedSet

SequencedSet<Integer> set = new LinkedHashSet<>();
set.add(100);
set.add(200);
set.add(300);

System.out.println("Primo elemento: " + set.getFirst()); // 100
System.out.println("Ultimo elemento: " + set.getLast()); // 300

SequencedSet<Integer> reversedSet = set.reversed();
System.out.println(reversedSet); // [300, 200, 100]

Esempio 4: lavorare con SequencedMap

SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("apple", 5);
map.put("banana", 3);
map.put("cherry", 7);

Map.Entry<String, Integer> first = map.firstEntry();
Map.Entry<String, Integer> last = map.lastEntry();

System.out.println("Primo: " + first.getKey() + " = " + first.getValue()); // apple = 5
System.out.println("Ultimo: " + last.getKey() + " = " + last.getValue()); // cherry = 7

SequencedMap<String, Integer> reversedMap = map.reversed();
System.out.println(reversedMap); // {cherry=7, banana=3, apple=5}

4. In che modo questo riguarda la vostra applicazione?

Supponiamo che nella vostra applicazione didattica conserviate l'elenco degli utenti che hanno effettuato l'accesso e vogliate ottenere rapidamente il primo e l'ultimo utente (per esempio, per mostrare «chi è entrato per primo» e «chi è l'ultimo»). Prima bisognava scrivere qualcosa del genere:

List<String> users = new ArrayList<>();
// ... aggiungiamo gli utenti
String first = users.get(0);
String last = users.get(users.size() - 1);

Ma se la collezione non è una lista, bensì per esempio un LinkedHashSet (dove gli elementi sono unici e l'ordine è conservato), questo trucco non funziona più:

Set<String> users = new LinkedHashSet<>();
// ... aggiungiamo gli utenti
// Come ottenere il primo? Solo tramite un iteratore:
String first = users.iterator().next();
// E l'ultimo? Bisogna scorrere tutti gli elementi!

Adesso è tutto più semplice e universale:

SequencedSet<String> users = new LinkedHashSet<>();
// ... aggiungiamo gli utenti
String first = users.getFirst();
String last = users.getLast();

Questo non solo riduce la quantità di codice, ma lo rende anche più leggibile e sicuro.

5. Schema delle nuove interfacce

classDiagram
    Collection <|-- SequencedCollection
    List <|-- SequencedCollection
    Set <|-- SequencedSet
    Map <|-- SequencedMap
    SequencedCollection <|-- SequencedSet
    SequencedSet <|-- LinkedHashSet
    SequencedSet <|-- TreeSet
    SequencedCollection <|-- ArrayList
    SequencedCollection <|-- LinkedList
    SequencedMap <|-- LinkedHashMap
    SequencedMap <|-- TreeMap

6. Dettagli utili

Vantaggi pratici di SequencedCollection

  • Un'interfaccia unificata per lavorare con l'ordine: Non serve più ricordare dove c'è get(0), dove è necessario un iteratore e dove non si può proprio ottenere il primo elemento.
  • Comodità nel lavorare con code e stack: È facile ottenere il primo (head) e l'ultimo (tail) elemento.
  • Sicurezza e leggibilità: Meno errori legati a un uso scorretto delle collezioni; il codice diventa autoesplicativo.
  • Inversione rapida della collezione: Il metodo reversed() consente di ottenere facilmente l'ordine inverso senza manipolazioni manuali.
  • Semplicità di manutenzione ed estensione del codice: Se in futuro vorrete sostituire, per esempio, ArrayList con LinkedHashSet, il codice che usa SequencedCollection non dovrà essere riscritto.

Caratteristiche di implementazione

  • Non tutte le collezioni implementano SequencedCollection: Per esempio, HashSet e HashMap non garantiscono l'ordine, quindi non implementano le nuove interfacce.
  • I metodi possono lanciare eccezioni: Se la collezione è vuota, una chiamata a getFirst() o getLast() provocherà una NoSuchElementException. Non dimenticate di verificare che la collezione non sia vuota!
  • reversed() è una vista, non una copia: Le modifiche alla collezione originale si riflettono nella vista invertita e viceversa.
  • Compatibilità: Le nuove interfacce sono disponibili solo a partire da Java 21. Se usate una versione precedente di Java, queste funzionalità non sono ancora disponibili (ma è un'ottima occasione per aggiornare!).
  • Generics: Tutte le nuove interfacce supportano completamente i generics, quindi si può lavorare con qualsiasi tipo di dato.

7. Errori tipici nell'uso di SequencedCollection

Errore n. 1: aspettarsi il supporto dell'ordine da collezioni che non lo garantiscono. Se provate a fare il cast di HashSet a SequencedSet, otterrete un errore di compilazione — HashSet non ha un ordine e non implementa questa interfaccia.

Errore n. 2: ignorare le collezioni vuote. Chiamare getFirst() o getLast() su una collezione vuota lancerà un'eccezione. Prima di usare questi metodi, verificate che la collezione non sia vuota:

if (!sc.isEmpty()) {
    String first = sc.getFirst();
}

Errore n. 3: fraintendimenti con reversed(). Il metodo reversed() restituisce una vista, non una copia. Se modificate la vista reversed, cambierà anche la collezione originale (e viceversa). Ciò può portare a risultati inattesi se non vi aspettate questo comportamento.

Errore n. 4: usare le nuove interfacce su versioni vecchie di Java. Se il vostro progetto è compilato con una versione inferiore a Java 21, il compilatore non troverà queste interfacce. Controllate la versione del JDK nelle impostazioni del progetto!

1
Compito
JAVA 25 SELF, livello 34, lezione 4
Bloccato
Gestore intelligente delle attività: verifica se vuoto prima di operare 🤖
Gestore intelligente delle attività: verifica se vuoto prima di operare 🤖
1
Compito
JAVA 25 SELF, livello 34, lezione 4
Bloccato
Algoritmo dei passaggi: controllo diretto e inverso 🔄
Algoritmo dei passaggi: controllo diretto e inverso 🔄
1
Sondaggio/quiz
Collezioni moderne, livello 34, lezione 4
Non disponibile
Collezioni moderne
Collezioni moderne e immutabilità
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION