Capitolo
2
Array
ANDREA GINI
Dopo aver introdotto i tipi primitivi, è giunto il momento di analizzare in profondità un altro
strumento importantissimo in un linguaggio di programmazione: l’array. Gli array (o vettori) sono
collezioni di variabili indicizzate, che permettono di gestire in maniera relativamente semplice
grosse porzioni di memoria, e di effettuare su di essa calcoli ripetitivi. Gli array tornano utili in
tutte le situazioni in cui si ha l’esigenza di manipolare un gruppo di variabili dello stesso tipo
che contengono valori tra loro correlati.
Si immagini di dover scrivere un programma che calcoli la media delle temperature giornaliere; se si dovessero prendere in considerazione 6 misurazioni all’ora, una ogni 10 minuti,
servirebbero ben 144 variabili.
int temp1 = 15;
int temp2 = 16;
int temp3 = 16;
int temp4 = 16;
....
int temp144 = 14;
// ore 0.00
// ore 0.10
// ore 0.20
// ore 0.30
// ore 11.50
Oltre alla scarsa praticità di dover dichiarare 144 variabili, non esiste nessun modo pratico
per effettuare calcoli che abbraccino tutto l’insieme dei valori: l’unico sistema per calcolare la
media sarebbe quello di realizzare una gigantesca espressione aritmetica del tipo:
int media = (temp1 + temp2 + temp3 + .... + temp143 + temp144) / 144;
In casi come questo è utile ricorrere a un array, uno strumento concettualmente simile a una
tabella, che accomuna sotto un unico nome un insieme di variabili dello stesso tipo:
10
Capitolo 2. Array
int[] temp = new int[144];
La creazione e l’utilizzo degli array presentano alcune differenze rispetto all’impiego delle
normali variabili. Nei prossimi paragrafi verranno illustrate una a una.
Dichiarazione di array
La dichiarazione di un array ha una sintassi un po’ più complessa della dichiarazione di variabile semplice. Come per le variabili è necessario indicare un tipo e un nome, con la differenza
che, dopo aver specificato il tipo, è necessario posporre una coppia di parentesi quadre:
int[] vettoreDiInteri;
Assegnamento
La variabile vettoreDiInteri appena dichiarata non è un vettore, ma solamente un riferimento
(in inglese, reference) a un vettore. Il vettore vero e proprio è un’entità separata, che occupa
un certo spazio nella memoria e che deve essere creata in modo opportuno. Prima di essere
inizializzata, la variabile ha il valore null, una costante che indica che la variabile non referenzia
alcun vettore.
Figura 2.1 – La variabile con cui si fa riferimento a un vettore ha inizialmente valore null.
VettoreDiInteri
Null
Per creare un vettore è necessario utilizzare la parola riservata new, come nell’esempio
seguente:
vettoreDiInteri = new int[10];
Il valore tra parentesi quadre è la dimensione del vettore: è possibile specificare un qualsiasi valore intero positivo. Il vettore appena creato è formato da dieci elementi, inizializzati a zero.
11
Manuale pratico di Java: dalla teoria alla programmazione
Figura 2.2 – Un vettore è un oggetto di memoria composto da un certo numero di elementi, ognuno
dei quali può contenere un valore.
0
0
0
0
VettoreDiInteri
0
0
0
0
0
0
Dereferenziazione
La dereferenziazione è l’operazione che permette di assegnare un valore a un elemento del
vettore. Per dereferenziare un elemento di un vettore, occorre specificare il nome dell’array
seguito dal numero dell’elemento tra parentesi quadre:
vettoreDiInteri[1] = 10;
Gli elementi di un vettore si contano a partire da zero: pertanto, se si desidera assegnare il
valore 27 al decimo elemento del vettore, è necessario scrivere:
vettoreDiInteri[9] = 27;
12
Capitolo 2. Array
Figura 2.3 – Lo stesso vettore della figura 2.2, dopo aver dereferenziato il secondo e il decimo
elemento.
0
10
0
0
VettoreDiInteri
0
0
0
0
0
27
Differenza tra assegnamento e dereferenziazione
Quando si lavora su vettori, bisogna avere ben chiara la differenza tra dereferenziazione e
assegnamento. L’assegnamento è un’operazione che agisce direttamente sulla variabile, provocandone un cambiamento di valore. La dereferenziazione, invece, è un’operazione indiretta:
essa non opera sulla variabile, ma sull’oggetto di memoria puntato da essa. Se si crea un nuovo
reference e gli si assegna il valore di un reference che punta a un vettore già esistente, si verifica
una situazione in cui due variabili puntano allo stesso vettore, come nell’esempio seguente
illustrato in figura 2.4:
int[] vettoreDiInteri2;
vettoreDiInteri2 = vettoreDiInteri;
13
Manuale pratico di Java: dalla teoria alla programmazione
Figura 2.4 – Due variabili che fanno riferimento allo stesso vettore.
0
10
0
0
VettoreDiInteri
0
0
VettoreDiInteri2
0
0
0
27
In una situazione come questa le operazioni vettoreDiInteri[5] = 10 e vettoreDiInteri[5] = 10
avranno entrambe il risultato di porre a 10 l’elemento numero 5 dell’unico vettore puntato dalle
due variabili. Per procedere all’effettiva copia di un vettore, è necessario dapprima creare un
vettore delle stesse dimensioni, quindi copiare uno a uno gli elementi del primo nel secondo.
Questa operazione può essere eseguita con un ciclo while, come nell’esempio seguente:
// crea un vettore e lo inizializza
int[] v1 = new int[5];
v1[0] = 10;
v1[1] = 12;
v1[2] = 14;
v1[3] = 16;
v1[4] = 18;
// crea un vettore della stessa dimensione di v1
int[] v2 = new int[5];
int i = 0;
while(i < 5) {
// copia il valore della i-esima cella
// di v1 nella i-esima cella di v2
v2[i] = v1[i];
}
14
Capitolo 2. Array
Inizializzazione automatica di un vettore
Un vettore può essere inizializzato con una serie di valori, in modo simile a come si può fare
con le variabili. L’istruzione:
int[] vettore = {10,12,14,16,18};
equivale alla sequenza di istruzioni:
int[] vettore = new int[5];
vettore[0] = 10;
vettore[1] = 12;
vettore[2] = 14;
vettore[3] = 16;
vettore[4] = 18;
Lunghezza di un vettore
I vettori creati con l’operatore new hanno esattamente la dimensione specificata nella dichiarazione. Gli indici sono numerati a partire da 0, per cui l’ultimo elemento avrà l’indice
pari alla dimensione del vettore meno uno. Per esempio, in un vettore da 10 elementi gli indici
sono compresi tra 0 e 9. Il programmatore può essere interessato a conoscere la dimensione
di un array a runtime: per questo scopo, ogni vettore dispone di un’apposita costante length,
accessibile tramite l’operatore ‘.’:
int vettoreDiInteri[] = new int[10];
System.out.print(“La dimensione del vettore è “);
System.out.println(vettoreDiInteri.length);
Un esempio di manipolazione di vettori
Il vettore è uno strumento potentissimo, che permette di lavorare su porzioni di memoria
anche molto grandi usando un numero ridotto di istruzioni. I cicli while permettono di valutare
uno a uno gli elementi di un array, e di effettuare qualche tipo di operazione su di essi. Per
calcolare la media dei valori contenuti in un ipotetico vettore vettoreDiInteri, si può usare un
frammento di codice di questo tipo:
int i = 0;
int somma = 0;
int media = 0;
while(i<vettoreDiInteri.length) {
Manuale pratico di Java: dalla teoria alla programmazione
15
somma = somma + vettoreDiInteri[i];
i++;
}
media = somma / sommaDiInteri.length;
Il seguente esempio permette di togliersi una soddisfazione: quella di scrivere un programma
che sfrutti una parte importante della memoria del proprio computer.
Un vettore di byte di dimensione 1024 occupa esattamente un kilobyte (KB) di memoria.
Un vettore di int della stessa dimensione ne occupa 4, dal momento che un int è grande 4 byte.
Un vettore di interi da 64 megabyte ha una dimensione che può essere calcolata moltiplicando
64 per 1048576 (pari a 1024 al quadrato) e dividendo per quattro. Una volta creato un simile
vettore, lo si può riempire di valori casuali scelti tra 0 e 10000; infine, è possibile calcolare la
somma di tutti i valori e la relativa media aritmetica. Si noti l’uso di una variabile di tipo long
per memorizzare la somma di tutti i numeri: è facile comprendere che una variabile intera non
avrebbe la dimensione sufficiente a contenere il risultato.
public class MemoryConsumer {
public static void main(String argv[]) {
long sum = 0;
long average = 0;
// calcola la dimensione del vettore.
// Se si dispone di poca memoria, impostare
// un valore più basso nella variabile megaBytes.
int megaBytes = 64;
int dim = megaBytes * 1048576 / 4;
int[] bigArray = new int[dim];
// riempie il vettore di valori casuali
int i = 0;
while ( i < bigArray.length ) {
bigArray[i] = (int)(32000 * Math.random());
i++;
}
// calcola la somma di tutti i valori
i = 0;
while ( i < bigArray.length ) {
sum = sum + bigArray[i];
i++;
}
// calcola la media
16
Capitolo 2. Array
average = sum / bigArray.length;
// stampa i risultati
System.out.print(“La somma di tutti i numeri presenti nel vettore è “);
System.out.println(sum);
System.out.print(“La media della somma di tutti i numeri presenti nel vettore è “);
System.out.println(average);
}
}
Il programma deve essere salvato, come di consueto, in un file dal nome “MemoryConsumer.java”;
per compilarlo bisogna digitare il comando:
javac MemoryConsumer.java
mentre per eseguirlo bisogna ricorrere all’istruzione:
java MemoryConsumer
Dopo qualche istante, il programma stamperà un output del tipo:
La somma di tutti i numeri presenti nel vettore è 239999200405
La media della somma di tutti i numeri presenti nel vettore è 15999
Per poter eseguire questo programma, è necessario disporre di un computer con almeno 256
MB di RAM. Se non si dispone di memoria sufficiente, il computer segnalerà un errore:
java.lang.OutOfMemoryError
<<no stack trace available>>
Exception in thread “main”
In questo caso, si provi a diminuire il valore della variabile ‘megaBytes’, portandolo per
esempio a 32, quindi si provi a compilare ed eseguire nuovamente.
Vettori multidimensionali
Il linguaggio Java consente di creare vettori bidimensionali, ricorrendo a una sintassi del tipo:
int i[][] = new int[10][15];
I vettori bidimensionali sono concettualmente simili a una tabella rettangolare, dotata di
righe e colonne. Il seguente programma crea una tavola pitagorica in un vettore bidimensionale,
quindi la stampa sullo schermo:
Manuale pratico di Java: dalla teoria alla programmazione
17
public class Tabelline2 {
public static void main(String argv[]) {
int[][] tabellina = new int[11][11];
// crea la tabellina in un vettore bidimensionale
int i = 0;
int j = 0;
while(i <= 10) {
while(j <= 10) {
int prodotto = i*j;
tabellina[i][j] = prodotto;
j = j + 1;
}
i = i + 1;
j = 0;
}
// stampa il contenuto del vettore
i = 0;
j = 0;
while(i <= 10) {
while(j <= 10) {
int prodotto = i*j;
System.out.print(tabellina[i][j]);
System.out.print(“\t”);
j = j + 1;
}
i = i + 1;
j = 0;
System.out.println();
}
}
}
È possibile definire vettori con un numero qualsiasi di dimensioni:
int v1[][][] = new int[10][15][5];
int v2[][][][] = new int[10][15][12][5];
Tali strutture, in ogni caso, risultano decisamente poco utilizzate.
Vettori incompleti
I vettori n-dimensionali vengono implementati in Java come array di array. Questa scelta
implementativa consente di realizzare tabelle non rettangolari, come nell’esempio seguente:
18
Capitolo 2. Array
// crea un vettore con una componente incompleta
int tabella[][] = new int[5][];
tabella[0] = new int[3];
tabella[1] = new int[2];
tabella[2] = new int[5];
tabella[3] = new int[2];
tabella[4] = new int[6];
Figura 2.5 – Rappresentazione in memoria di un vettore non rettangolare.
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Per dichiarare un vettore incompleto come quello dell’esempio è necessario specificare la
prima componente in fase di creazione, mentre la seconda verrà precisata successivamente
creando uno a uno i sotto-vettori. Anche in questo caso ci si trova in presenza di un costrutto
scarsamente utilizzato, che tuttavia vale la pena di conoscere.
Inizializzazione automatica di un vettore multidimensionale
Un vettore può essere inizializzato con una serie di valori, in modo simile a come si può fare
con i vettori semplici. Naturalmente è necessario ricorrere a un costrutto un po’ più complesso,
che tenga conto della particolare struttura di questi vettori. La seguente istruzione, per esempio,
crea un vettore a tre componenti in verticale, in cui la prima riga ha tre colonne, la seconda due
19
Manuale pratico di Java: dalla teoria alla programmazione
e la terza quattro, e contemporaneamente inizializza gli elementi con i valori specificati, come
si può vedere in figura 2.6:
int[][] vettore = { { 10,12,14} , {16,18} , {20,22,24,26} };
Figura 2.6 – Un altro esempio di vettore non rettangolare.
10
12
16
18
20
22
14
24
26