W3docs

Interfaccia List in Java

Collezioni ordinate e accessibili per indice in Java con l'interfaccia List e le sue operazioni principali.

List<E> è Collection<E> con due impegni aggiuntivi: gli elementi hanno un ordine definito, e quell'ordine è indirizzabile tramite indice intero. Una volta che si dispone di ordine e indice, un'intera classe di metodi diventa significativa — get(i), set(i, x), indexOf(x), subList(from, to), ordinamento, iterazione inversa. Questo capitolo percorre il contratto; le implementazioni (ArrayList, LinkedList, Vector) seguono immediatamente dopo con i propri compromessi di prestazioni.

Cosa significa "ordinato" in questo contesto

"Ordinato" su una List significa che l'ordine di inserimento viene preservato — l'indice 0 è il primo elemento inserito, l'indice size() - 1 è l'ultimo, e aggiungere un nuovo elemento alla fine non sposta nulla. Non significa "ordinato in modo crescente" — una lista mantiene qualsiasi ordine si produca. Se si vuole un'iterazione ordinata, si chiama Collections.sort(list) (che muta), oppure si usa TreeSet / TreeMap dall'inizio. Non confondere le due cose.

I duplicati sono consentiti. [1, 1, 2, 1] è una List<Integer> perfettamente valida.

I metodi che List aggiunge rispetto a Collection

Tutto ciò che Collection dichiara è ancora presente — add, remove, contains, size, ecc. List aggiunge poi operazioni posizionali e consapevoli dell'ordine:

Accesso posizionale

  • E get(int index) — elemento all'index.
  • E set(int index, E element) — sostituisce, restituendo il vecchio valore.
  • void add(int index, E element) — inserisce (sposta gli elementi successivi a destra).
  • E remove(int index) — rimuove per posizione (restituisce l'elemento rimosso). Nota l'overload con Objectlist.remove(1) chiama la versione int; list.remove(Integer.valueOf(1)) chiama la versione Object.

Ricerca

  • int indexOf(Object o) — prima occorrenza, o -1.
  • int lastIndexOf(Object o) — ultima occorrenza, o -1.

Sotto-viste e iterazione

  • List<E> subList(int fromIndex, int toIndex) — una vista live di un intervallo. Modificarla modifica la lista di supporto (e viceversa). Semiaperto: [from, to).
  • ListIterator<E> listIterator() / listIterator(int index) — iteratore che può anche scorrere all'indietro, ottenere l'indice corrente e set / add al cursore. Il capitolo ListIterator lo tratta in dettaglio.

Mutazione collettiva legata all'ordine

  • default void replaceAll(UnaryOperator<E> op) — applica op a ogni elemento sul posto.
  • default void sort(Comparator<? super E> c) — ordina la lista usando c (o l'ordine naturale se null).
  • boolean addAll(int index, Collection<? extends E> c) — inserisce un'intera collezione all'index.

Factory (Java 9+)

  • List.of(...) — una lista non modificabile degli elementi dati. Compatta, senza allocazione per dimensioni piccole.
  • List.copyOf(Collection) — uno snapshot non modificabile di un'altra collezione.

L'uguaglianza su List è sensibile all'ordine

Due liste sono uguali se e solo se hanno la stessa dimensione, nello stesso ordine, con elementi uguali a ogni indice. List.of(1, 2) non è uguale a List.of(2, 1), anche se come Set lo sarebbero. Questa è una regola rigida del contratto List — se si confrontano due liste e si ottiene false quando "non dovrebbe essere così", controllare prima l'ordine.

subList è una vista, non una copia

Questo inganna quasi ogni studente almeno una volta:

List<Integer> xs = new ArrayList<>(List.of(0, 1, 2, 3, 4, 5));
List<Integer> middle = xs.subList(2, 5);    // [2, 3, 4]
middle.set(0, 99);
System.out.println(xs);          // [0, 1, 99, 3, 4, 5]   — xs changed!
middle.clear();
System.out.println(xs);          // [0, 1, 5]            — gone from xs

subList restituisce una finestra live. Le letture e le scritture passano attraverso alla lista di supporto. Questo è estremamente utile per gli algoritmi in-place — svuotare un intervallo, ordinare un intervallo, inserire un intervallo — ma significa anche che non si può tenere un riferimento a subList e poi mutare il genitore attraverso qualsiasi altro percorso. Il Javadoc afferma che le modifiche strutturali alla lista di supporto al di fuori della sotto-lista "indefiniscono" il comportamento della sotto-lista. In pratica, ConcurrentModificationException alla chiamata successiva.

Se si vuole uno slice indipendente, copiarlo: new ArrayList<>(xs.subList(2, 5)).

I due overload di remove

Un bug comune:

List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1);                       // removes index 1 → [10, 30]
nums.remove(Integer.valueOf(10));     // removes the value 10 → [30]

L'overload int vince perché int è più specifico di Integer. Se si intende "rimuovere il valore 10", eseguire il boxing esplicitamente. Questo è uno dei pochi punti del linguaggio in cui le regole di autoboxing e la risoluzione degli overload entrano attivamente in conflitto.

Ordinamento sul posto

list.sort(comparator) muta la lista. Passare null per usare l'ordine naturale degli elementi (il loro Comparable); passare un Comparator altrimenti. Questa è la forma moderna — Collections.sort(list) funziona ancora ed è identica, ma list.sort(...) è il metodo predefinito di List:

List<String> names = new ArrayList<>(List.of("Linus", "Ada", "Grace"));
names.sort(null);                              // natural: ["Ada", "Grace", "Linus"]
names.sort(Comparator.comparingInt(String::length));  // shortest first

Il capitolo Comparable / Comparator più avanti in questa parte è il manuale su cosa significa null e come costruire comparatori per i propri tipi.

Factory immutabili: quando add lancia un'eccezione

List.of(...), List.copyOf(...) e le liste restituite da Collectors.toUnmodifiableList() sono non modificabili. Rifiutano ogni chiamata mutante con UnsupportedOperationException. Rifiutano anche gli elementi null. Sono ideali per dati in sola lettura condivisi ampiamente:

List<String> CONSTANTS = List.of("red", "green", "blue");
CONSTANTS.add("yellow");    // throws UnsupportedOperationException

Se si potrebbe voler mutare in seguito, iniziare con new ArrayList<>(List.of(...)).

Un esempio pratico: ogni metodo specifico di List

Il programma seguente esercita i metodi che List aggiunge rispetto a Collection. Osservare la propagazione della mutazione di subList, la trappola degli overload e la differenza tra sort e replaceAll.

java— editable, runs on the server

Alcune considerazioni da tenere a mente dall'output:

  • remove(1) ha rimosso 20 (il valore all'indice 1); remove(Integer.valueOf(10)) ha rimosso 10 per valore. Stesso nome di metodo, due lavori diversi in base al tipo statico dell'argomento.
  • Dopo mid.clear(), la lista genitore è [0, 1, 5]. La vista era quell'intervallo — svuotarla ha rimosso quegli elementi dall'array di supporto.
  • replaceAll mantiene la lista della stessa lunghezza e riscrive ogni elemento sul posto; sort riorganizza ciò che è già presente. Si combinano bene.

Cosa c'è dopo

Ora si conosce il contratto List — cosa è garantito, cosa muta cosa, dove sono le insidie. È il momento di conoscere l'implementazione che si userà il 90% delle volte: l'ArrayList basata su array ridimensionabile. Stesso contratto, caratteristiche di prestazioni specifiche, e qualche extra in più.

Pratica

Pratica
`xs` è `new ArrayList<>(List.of(10, 20, 30, 40))`. Si chiama `xs.subList(1, 3).clear()`. Cosa contiene `xs` dopo?
`xs` è `new ArrayList<>(List.of(10, 20, 30, 40))`. Si chiama `xs.subList(1, 3).clear()`. Cosa contiene `xs` dopo?
Was this page helpful?