|
Le classi java.util.Vector e java.util.ArrayList definiscono degli oggetti, chiamati vettori, che consentono di rappresentare sequenze di oggetti di lunghezza variabile. Ciascun oggetto in un vettore ha un numero intero, detto indice, che ne indica la posizione nel vettore. Grazie all'indice è possibile accedere indipendentemente a ciascun elemento della sequenza. L'accesso ad una posizione inesistente provoca un errore (viene lanciata un'eccezione). I vettori sono simili agli array. Le differenze principali sono tre:
Al momento della dichiarazione o creazione di un vettore, il tipo base degli elementi del vettore va indicato tra parentesi angolate dopo il nome della classe: Vector e ArrayList sono classi generiche, un concetto che approfondiremo tra breve. Nell'esempio che segue, il primo comando costruisce un vettore di stringhe (inizialmente vuoto) e lo assegna alla variabile vet; il secondo crea invece un vettore di BankAccount.
|
|
A partire dalla versione 5.0, sono stati introdotti in Java i tipi generici, cioè classi e
interfacce con parametri di tipo, che possono essere istanziati con
arbitrarie classi Java.
Vediamo un primo esempio di tipo generico: anche l'interfaccia Comparable che avevamo visto è in realtà parametrica rispetto al tipo degli elementi che possono essere confrontati.
Comparable<T> è un'interfaccia generica, poiché ha un parametro di tipo T. <T> è una dichiarazione di parametro formale di tipo. Dopo la dichiarazione, T può essere usato nel codice della classe come se fosse un tipo noto. L'uso corretto di interfacce o classi generiche prevede l'opportuna istanziazione dei parametri di tipo, come nelle seguenti dichiarazioni di classi.
Si noti che viene segnalato un errore a tempo di compilazione, invece che durante l'esecuzione. L'errore segnalato è
|
|
L'implementazione dei vettori è basata sugli array.
Al momento della creazione di un vettore, alla variabile di istanza
elementData
viene assegnato un array di oggetti la cui dimensione dipende dal costruttore utilizzato:
Nota: indipendentemente dal parametro di tipo del vettore, l'array usato nell'implementazione è di oggetti della classe Object. Tutti i controlli di consistenza sui generici sono demandati all'analisi statica del compilatore. Successivamente, se la capacità dell'array non è più sufficiente, viene creato un nuovo array più grande nel quale vengono copiati tutti gli elementi del vecchio.
|
|
Nel seguito, usando la sintassi Java 5.0,
indicheremo con Vector<E> e ArrayList<E> le classi
generiche che definiscono i vettori quando il tipo base non è
specificato.
Le classi generiche Vector<E> e ArrayList<E> sono sostanzialmente equivalenti, ma:
Descrizione completa delle classi: Vector [ Sun] e ArrayList [ Sun]. |
|
I costruttori di Vector<E>
permettono di specificare la capacità
iniziale del vettore (initialCapacity)
e il valore da usare per aumentare la capacità
(capacityIncrement) quando
necessario. Se (capacityIncrement == 0), il nuovo array
avrà capacità doppia rispetto all'attuale.
I costruttori di ArrayList<E> permettono di specificare solo la capacità iniziale del vettore.
|
|
I metodi che operano sui vettori consentono tra l'altro di:
Esempio: EsempioVettore.java
|
|
Sottolineiamo alcune differenze sintattiche nell'uso di un vettore al
posto di un array. Si noti che il for generalizzato può essere usato anche per scandire gli elementi di un vettore: la sintassi è identica a quella vista per scandire un array.
|
|
In molte situazioni è utile memorizzare dei dati
in un array, anche se il numero di dati può variare. In questo
caso si può usare un array parzialmente riempito (si veda anche
l'Horstmann).
Per copiare in modo efficiente (parte di) un array in un altro si può usare il metodo statico
Questo metodo può essere usato anche per spostare degli elementi all'interno di uno stesso array, fornendolo sia come primo che come terzo argomento. |
|
Come anticipato, il tipo base di un vettore
non può essere un tipo primitivo: quindi è illegale
ad esempio dichiarare una variabile di tipo ArrayList<int> oppure Vector<double>.
Java fornisce tuttavia le cosiddette classi involucro o classi contenitore, che possono essere usate come tipo base di vettori in cui si vogliano memorizzare numeri interi, numeri in virgola mobile, o valori booleani. Le istanze di una classe involucro sono usate per incapsulare valori primitivi all'interno di un oggetto. Questo permette di trattare in maniera omogenea dati di tipi primitivi ed oggetti. La conversione tra un tipo di dati primitivo e la corrispondente classe involucro è automatica, grazie al meccanismo di autoboxing/unboxing introdotto in Java 5.0. |
|
Ci sono classi involucro per tutti i tipi primitivi. Normalmente
hanno un nome simile al corrispondente tipo primitivo, ma iniziano con
maiuscola:
|
|
La classe Integer
è la classe involucro per numeri interi. Grazie all'autoboxing/unboxing
è possibile assegnare valori di tipo int a variabili di tipo Integer
e viceversa.
Nel primo comando viene invocato implicitamente un costruttore che crea un oggetto di tipo Integer a partire da un valore di tipo int. Nel secondo caso viene invocato implicitamente sull'oggetto di tipo Integer il metodo intValue(). Quindi i due comandi visti sono equivalenti ai seguenti:
La classe Integer fornisce diversi altri membri. Tra questi ricordiamo le costanti Integer.MAX_VALUE (2147483647) e Integer.MIN_VALUE (-2147483648), che sono il più grande e il più piccolo valore rappresentabile in una variabile di tipo int, e il metodo statico parseInt(), che permette di convertire una stringa in un intero:
Un metodo analogo è il metodo parseDouble() della classe Double, che converte una stringa nel numero in virgola mobile che rappresenta. Come abbiamo visto, questi metodi lanciano un'eccezione NumberFormatException se la stringa su cui sono invocati non rappresenta un numero. |
|
Vediamo ora un semplice esempio di manipolazione di
un vettore di interi, ProvaVectorInteger.java. Si osservi che il tipo base del vettore
è Integer.
Si osservi che, grazie alla conversione automatica tra Integer e int, il codice è simile a quello che avremmo scritto usando un array di int al posto del vettore. A livello di esecuzione del codice, tuttavia, le differenze sono notevoli. Infatti mentre un assegnamento come arr[i] = n comporta semplicemente la copia del valore della variabile n nella variabile arr[i], l'esecuzione del comando vet.add(n) causa la creazione di un nuovo oggetto di tipo Integer e l'invocazione del metodo add. |