La classe ArrayList in Java

Concetti Chiave
  • La classe ArrayList estende AbstractList e implementa l'interfaccia List.
  • ArrayList è una classe generica che supporta array dinamici che possono crescere secondo necessità.
  • Gli array standard in Java hanno una lunghezza fissa, ma ArrayList può aumentare o diminuire dinamicamente la propria dimensione.
  • ArrayList ha diversi costruttori per creare liste vuote, inizializzate con una collezione o con una capacità specificata.

La Classe ArrayList

La classe ArrayList estende AbstractList e implementa l'interfaccia List. ArrayList è una classe generica che ha questa dichiarazione:

class ArrayList<E>

Qui, E specifica il tipo di oggetti che la lista conterrà.

ArrayList supporta array dinamici che possono crescere secondo necessità. In Java, gli array standard hanno una lunghezza fissa. Dopo che gli array sono creati, non possono crescere o ridursi, il che significa che bisogna sapere in anticipo quanti elementi un array conterrà. Ma, a volte, potreste non sapere fino al momento dell'esecuzione precisamente quanto grande deve essere un array di cui avete bisogno. Per gestire questa situazione, il Collections Framework definisce ArrayList. In sostanza, un ArrayList è un array di riferimenti a oggetti di lunghezza variabile. Cioè, un ArrayList può aumentare o diminuire dinamicamente di dimensione. Le liste di array sono create con una dimensione iniziale. Quando questa dimensione viene superata, la collezione viene automaticamente ingrandita. Quando gli oggetti vengono rimossi, l'array può essere ridotto.

ArrayList ha i costruttori mostrati qui:

ArrayList()
ArrayList(Collection<? extends E> c)
ArrayList(int capacity)

Il primo costruttore costruisce una lista di array vuota. Il secondo costruttore costruisce una lista di array che è inizializzata con gli elementi della collezione c. Il terzo costruttore costruisce una lista di array che ha la capacity iniziale specificata. La capacity è la dimensione dell'array sottostante che viene utilizzato per memorizzare gli elementi. La capacity cresce automaticamente man mano che gli elementi vengono aggiunti a una lista di array.

Il seguente programma mostra un uso semplice di ArrayList. Viene creata una lista di array per oggetti di tipo String, e poi diverse stringhe vengono aggiunte ad essa. (Si ricordi che una stringa tra virgolette viene tradotta in un oggetto String). La lista viene poi visualizzata. Alcuni degli elementi vengono rimossi e la lista viene visualizzata di nuovo.

// Dimostra ArrayList.
import java.util.*;

class DemoArrayList {

    public static void main(String[] args) {
        // Crea un ArrayList di Stringhe
        ArrayList<String> al = new ArrayList<String>();

        System.out.println("Dimensione iniziale di al: " +
                           al.size());

        // Aggiunge elementi all'ArrayList.
        al.add("C");
        al.add("A");
        al.add("E");
        al.add("B");
        al.add("D");
        al.add("F");
        al.add(1, "A2");

        System.out.println("Dimensione di al dopo le aggiunte: " +
                           al.size());

        // Visualizza l'ArrayList.
        System.out.println("Contenuti di al: " + al);

        // Rimuove elementi dall'ArrayList.
        al.remove("F");
        al.remove(2);

        System.out.println("Dimensione di al dopo le eliminazioni: " +
                           al.size());
        System.out.println("Contenuto di al: " + al);
    }

}

L'output di questo programma è mostrato qui:

Dimensione iniziale di al: 0
Dimensione di al dopo le aggiunte: 7
Contenuti di al: [C, A2, A, E, B, D, F]
Dimensione di al dopo le eliminazioni: 5
Contenuto di al: [C, A2, E, B, D]

Si noti che al inizia vuota e cresce man mano che gli elementi vengono aggiunti ad essa. Quando gli elementi vengono rimossi, la sua dimensione viene ridotta.

Nell'esempio precedente, i contenuti di una collezione vengono visualizzati utilizzando la conversione predefinita fornita da toString(), che è stata ereditata da AbstractCollection. Sebbene sia sufficiente per programmi di esempio brevi, raramente si utilizza questo metodo per visualizzare i contenuti di una collezione del mondo reale. Di solito, bisogna fornire le proprie routine di output. Ma, per i prossimi esempi, l'output predefinito creato da toString() è sufficiente.

Sebbene la capacity di un oggetto ArrayList aumenti automaticamente quando gli oggetti vengono memorizzati in esso, si può aumentare la capacity di un oggetto ArrayList manualmente chiamando ensureCapacity(). Si potrebbe voler fare questo se si conosce in anticipo che verranno memorizzati molti più elementi nella collezione di quanti ne possa attualmente contenere. Aumentando la sua capacity una volta, all'inizio, si possono prevenire diverse riallocazioni successive. Poiché le riallocazioni sono costose in termini di tempo, prevenire quelle non necessarie migliora le prestazioni. La firma per ensureCapacity() è mostrata qui:

void ensureCapacity(int cap)

Qui, cap specifica la nuova capacity minima della collezione.

Al contrario, se si vuole ridurre la dimensione dell'array che fornisce il supporto di un oggetto ArrayList in modo che sia precisamente grande quanto il numero di elementi che sta attualmente contenendo, bisogna chiamare trimToSize(), mostrato qui:

void trimToSize()

Ottenere un Array da un ArrayList

Quando lavoriamo con ArrayList, a volte vorremo ottenere un array effettivo che contenga i contenuti della lista. Possiamo farlo chiamando toArray(), che è definito da Collection.

Esistono diverse ragioni per cui potremmo voler convertire una collezione in un array, ad esempio:

  • Per ottenere tempi di elaborazione più veloci per certe operazioni;
  • Per passare un array a un metodo che non è sovraccaricato per accettare una collezione;
  • Per integrare codice basato su collezioni con codice legacy che non comprende le collezioni.

Qualunque sia la ragione, convertire un ArrayList in un array è una questione banale.

Come spiegato precedentemente, ci sono tre versioni di toArray(), che vengono mostrate di nuovo qui per comodità:

Object[] toArray()
<T> T[] toArray(T[] array)
default <T> T[] toArray(IntFunction<T[]> arrayGen)

La prima restituisce un array di Object. La seconda e terza forma restituiscono un array di elementi che hanno lo stesso tipo di T. Qui, useremo la seconda forma a causa della sua comodità. Il seguente programma la mostra in azione.

// Convertire un ArrayList in un array.
import java.util.*;

class ArrayListVersoArray {

    public static void main(String[] args) {
        // Crea un ArrayList di Integer.
        ArrayList<Integer> al = new ArrayList<Integer>();

        // Aggiunge alcuni elementi all'ArrayList.
        al.add(1);
        al.add(2);
        al.add(3);
        al.add(4);

        System.out.println("Contenuto di al: " + al);

        // Ottiene un array di Integer dall'ArrayList.
        Integer[] ia = new Integer[al.size()];
        ia = al.toArray(ia);

        // Effettua la somma degli elementi dell'array.
        int somma = 0;
        for (int i : ia)
            somma += i;
        System.out.println("La somma è: " + somma);
    }

}

L'output del programma è mostrato qui:

Contenuto di al: [1, 2, 3, 4]
La somma è: 10

Il programma inizia creando una collezione di interi. Successivamente, toArray() viene chiamato e ottiene un array di Integer. Poi, i contenuti di quell'array vengono sommati usando un ciclo for in stile for-each.

C'è qualcos'altro di interessante in questo programma. Come sappiamo, le collezioni possono memorizzare solo riferimenti, non valori di tipi primitivi. Tuttavia, l'autoboxing rende possibile passare valori di tipo int a add() senza dover manualmente avvolgerli dentro un Integer, come mostra il programma. L'autoboxing fa sì che vengano automaticamente avvolti. In questo modo, l'autoboxing migliora significativamente la facilità con cui le collezioni possono essere usate per memorizzare valori primitivi.