W3docs

Panoramica del Java Collections Framework

Una panoramica del Java Collections Framework: interfacce, implementazioni e algoritmi in java.util.

Quasi ogni programma finisce per contenere un gruppo di elementi — ordini in attesa di spedizione, parole in un documento, utenti attualmente online, la coda di attività che un thread worker eseguirà. La risposta di Java alla domanda "dove metto quel gruppo?" è il Collections Framework: un insieme coordinato di interfacce in java.util, una dozzina o più di implementazioni dietro di esse e un'unica classe di utilità con algoritmi (Collections) che lavora sulle interfacce anziché su una specifica implementazione. Questo capitolo è la mappa — cosa c'è nel framework, perché è strutturato così e quale famiglia usare in quale situazione. Ogni capitolo di questa parte è un approfondimento su uno dei riquadri di quella mappa.

Perché un framework, non solo delle classi

Prima di Java 1.2 esistevano classi per i gruppi (Vector, Hashtable, Stack) ma nessuna interfaccia condivisa — non si poteva scrivere un metodo che accettasse "qualsiasi lista" e funzionasse con tutte e tre. Il Collections Framework ha risolto il problema separando cosa è un gruppo (l'interfaccia) da come viene memorizzato (l'implementazione). Il vantaggio è evidente ogni giorno:

// You program against the interface, not the class.
List<String> names = new ArrayList<>();
// Later, swap in a different implementation without touching the rest of the code:
List<String> names = new LinkedList<>();

Ogni metodo che chiami su names è definito su List. La scelta tra ArrayList e LinkedList è una decisione di prestazioni, non di API. Puoi anche scrivere metodi come void print(List<String> xs) una sola volta e passare entrambe le implementazioni.

Le quattro interfacce radice

Il framework è organizzato attorno a quattro interfacce radice. Scegli quella il cui contratto corrisponde alla natura dei tuoi dati:

  • Collection<E> — la radice. Un gruppo di elementi E. Non dice nulla sull'ordine, i duplicati o l'indicizzazione.
  • List<E> — una raccolta ordinata accessibile per indice. Duplicati consentiti. Pensa a "array che cresce" o "sequenza concatenata." ArrayList, LinkedList, Vector.
  • Set<E> — una raccolta che vieta i duplicati. Insieme matematico. Può essere ordinata o meno. HashSet, LinkedHashSet, TreeSet.
  • Map<K, V> — ricerca chiave→valore. Non è una Collection (i suoi elementi sono entry, non valori singoli), ma fa parte del framework. HashMap, LinkedHashMap, TreeMap.

Altre due si affiancano a List e Set come specializzazioni di Collection:

  • Queue<E> — una raccolta "prossimo in fila". FIFO per default. LinkedList, ArrayDeque, PriorityQueue.
  • Deque<E> — una coda a doppia estremità. Aggiunta e rimozione a entrambe le estremità. ArrayDeque, LinkedList.

Ogni classe di collezione nella libreria standard implementa almeno una di queste interfacce.

Scegli la famiglia in base al comportamento, poi la classe in base al costo

La decisione si articola su due livelli:

  1. Di cosa ha bisogno il dato? Ordinato con duplicati → List. Senza duplicati → Set. Ricerca per chiave → Map. Produttore/consumatore → Queue o Deque.
  2. All'interno della famiglia, quali sono i pattern di accesso? Accesso casuale per indice → ArrayList. Inserimento/rimozione frequente in testa → LinkedList o ArrayDeque. Iterazione ordinata → TreeSet / TreeMap. Semplicemente "ho bisogno di un contenitore veloce" → HashSet / HashMap.

Un rapido cheat sheet per le soluzioni più comuni:

NecessitàUsaNote
Lista ridimensionabile, accesso casuale veloceArrayListLa List predefinita.
Lista con inserimento/rimozione molto frequente alle estremitàArrayDeque (come coda simile a una lista)Supera LinkedList in pratica.
Set, contains/add/remove velociHashSetNessuna garanzia di ordine.
Set con iterazione prevedibileLinkedHashSetIterazione in ordine di inserimento.
Set ordinatoTreeSetOperazioni O(log n).
Map, ricerca veloceHashMapLa Map predefinita.
Map con iterazione prevedibileLinkedHashMapUtile per cache (LRU).
Map ordinataTreeMapChiavi in ordine ordinato.
Coda FIFOArrayDequePiù veloce di LinkedList.
Estrai sempre il più piccoloPriorityQueueBacked da heap.

Vector, Stack e Hashtable sono le classi pre-1.2 — ancora presenti nel JDK per compatibilità con il passato, ma non più la scelta giusta nel codice nuovo. Le loro sostituzioni moderne sono trattate nei rispettivi capitoli.

I generics rendono le collections type-safe

Ogni interfaccia e classe di collezione è generica. La parametrizzi con il tipo dell'elemento al momento della dichiarazione, e il compilatore la verifica da quel momento in poi:

List<String> names = new ArrayList<>();
names.add("Ada");
names.add(42);          // ❌ compile error — Integer is not a String
String first = names.get(0);   // no cast needed

Il diamante <> a destra chiede al compilatore di inferire il tipo da sinistra — risparmia digitazione senza perdere la sicurezza dei tipi. La parte precedente del libro (Generics) contiene le regole alla base di tutto questo; il resto di questa parte presuppone che tu l'abbia letta.

Gli algoritmi si trovano nella classe Collections

Le operazioni non legate a una specifica implementazione — sort, shuffle, reverse, binary-search, min, max, frequency, wrapper non modificabili — si trovano nella classe di utilità statica java.util.Collections. Nota la s finale: Collection (l'interfaccia) è un elemento; Collections (la classe) è il toolkit:

List<Integer> xs = new ArrayList<>(List.of(3, 1, 4, 1, 5, 9, 2, 6));
Collections.sort(xs);                    // mutates xs
int idx = Collections.binarySearch(xs, 5);
List<Integer> ro = Collections.unmodifiableList(xs);

Dedichiamo tre capitoli verso la fine di questa parte a Collections — ricerca, ordinamento e wrapper non modificabili — perché la classe di utilità è dove avviene molto del lavoro pratico.

Un primo tour completo

Il programma sottostante sceglie un rappresentante per ogni famiglia e mostra le operazioni che userai continuamente. Non preoccuparti di capire ogni riga ancora — ogni classe qui ha il suo capitolo. L'obiettivo è vedere la struttura del framework: stessi nomi di metodi, stessi pattern, stesso modello di iterazione.

java— editable, runs on the server

Nota tre cose nell'output:

  1. Il Set ha eliminato silenziosamente il duplicato "Effective Java". Questo è il contratto — nessun codice aggiuntivo da parte tua.
  2. La Queue ha restituito gli elementi nell'ordine in cui sono stati aggiunti. offer accoda, peek guarda la testa senza rimuovere, poll rimuove e restituisce.
  3. Il ciclo for-each funziona su ogni Collection. Le mappe iterano tramite entrySet(), keySet() o values() a seconda di cosa vuoi.

Questo è il framework in miniatura.

Attenzione

HashSet e HashMap non danno alcuna garanzia sull'ordine di iterazione — l'ordine che vedi nell'output sopra è un dettaglio implementativo e può variare tra versioni della JVM o esecuzioni diverse. Se hai bisogno di un ordine stabile, usa LinkedHashSet/LinkedHashMap (ordine di inserimento) o TreeSet/TreeMap (ordine ordinato). Non scrivere mai codice che dipende dall'ordine di iterazione di un semplice HashSet o HashMap.

Cosa c'è dopo

Hai visto la mappa. Il resto della Parte 11 percorre ciascuna regione. Il punto di partenza naturale è la radice da cui ogni collezione eredita — l'interfaccia Collection stessa, dove metodi come add, remove, contains, size e iterator() sono definiti una volta per tutte.

Esercitazione

Pratica
Devi memorizzare un gruppo di parole e rispondere rapidamente alla domanda 'questa parola è nel gruppo?'. I duplicati non sono rilevanti — `cat` o compare o non compare. Quale famiglia del Collections Framework è la scelta naturale?
Devi memorizzare un gruppo di parole e rispondere rapidamente alla domanda 'questa parola è nel gruppo?'. I duplicati non sono rilevanti — `cat` o compare o non compare. Quale famiglia del Collections Framework è la scelta naturale?
Was this page helpful?