Java: Gli Array ad oggetti ed ArrayList

Lettura e stampa di un array


Supponiamo di voler riempire un array di interi con valori letti da input.


  • Per riempire un array dobbiamo prima crearlo,
  • per crearlo dobbiamo conoscerne la dimensione.
Quindi come prima cosa bisogna leggere la dimensione dell'array, in seguito dobbiamo leggere i singoli valori con i quali riempire le celle dell'array.
Per accedere agli elementi di un array a usiamo un ciclo  for con una variabile di controllo (usata come indice) che assume i valori da 0 al valore di a.length-1 (estremi inclusi).


// Riempie un  array di dimensione  specificata dall' utente,
// con valori anch'essi forniti dall'utente
public class RiempiArray { 

    public static void main (String[] args) {

        System.out.print("Quanti elementi ha l'array? (un intero > 0) ");
        int n = Input.readInt(); // legge la dimensione

        int[] array = new int[n]; // dichiara l'array

        // riempie l'array
        for (int i = 0; i < n; i++) {
            System.out.print("Dammi l'" + (i+1) +"-esimo  intero: ");
            array[i] = Input.readInt();
        }
    }
}

Supponiamo adesso di voler anche stampare l' array dopo averlo costruito.Un array a è un oggetto e se si tenta di stamparlo direttamente con System.out.println(a) il risultato non è quello atteso. Ad esempio, si provi a eseguire il seguente programma:


public class StampaErrata {
    public static void main (String[] args) {
        int[] a = { 1, 2, 3 };
        System.out.println(a); 
        // Stampa l'indirizzo di memoria dell'oggetto-array a 
    }
}
Per stampare l'array possiamo usare un ciclo for che stampa tutti gli elementi dell'array (separati da spazi), delimitati dalle parentesi quadre:


public class RiempiStampaArray {
    public static void main (String[] args) {
        System.out.print("Quanti elementi ha l'array? (un intero > 0) ");
        int n = Input.readInt(); // legge la dimensione

        int[] array = new int[n]; // dichiara l'array

        // riempie l'array
        for (int i = 0; i < n; i++) {
            System.out.print("Dammi l'" + (i+1) +"-esimo intero: ");
            array[i] = Input.readInt();
            }
        // stampa l'array
        System.out.print("[ ");
        for (int i = 0; i < array.length; i++)
            System.out.print(array[i] +  " ");
        System.out.println("]");
    }
}





Lettura e scrittura di array come metodi


La parte del codice del programma RiempiStampaArray che riempie l'array, o quella che ne stampa gli elementi potrebbero essere utili in altri programmi, ma non sono riutilizzabili: andrebbero copiate.
Riscriviamole come metodi (statici) di una opportuna classe, ArrayUtils, in modo da poterli riusare liberamente. Si noti che:
  • i metodi non possono fare assunzioni sui valori passati come parametri, quindi devono considerare anche i casi limite;
  • il metodo fill_int restituisce un array di int come risultato;

  • al metodo print passiamo come parametro l'array da stampare. Tale metodo non è tipizzato.


public class ArrayUtils { 

    // Chiede la dimensione dell'array, assicurandosi che sia un
    // valore positivo e restituisce un array di interi della
    // dimensione letta, chiedendo i valori all'utente.
    public static int[] fill_int () {
        int n = 0;
        do {
            System.out.print("Dimensione array (intero positivo): ");
            n = Input.readInt();
        } while (n<=0);
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            System.out.print("Dammi l'intero in posizione " + i + ": ");
            array[i] = Input.readInt();
        }
        return array;
    }

    public static void print (int[] array) {
        System.out.print("[ ");
        for (int i = 0; i < array.length; i++)
            System.out.print(array[i] +  " ");
        System.out.println("]");
    }
}
Per testare i metodi occorre scrivere una classe principale (cioè con il metodo main):


public class TestRiempiStampaArray { 
    public static void main (String[] args) {
        int[] a = ArrayUtils.fill_int();
        ArrayUtils.print(a);
   }
}





 Esempi di programmi utili su array


Un tipico uso di un array consiste nello scandire (o visitare) in sequenza i suoi elementi, per esaminarli, modificarli, o confrontarli...Come abbiamo già visto per la lettura e scrittura, per scandire TUTTI gli elementi di un array a è naturale usare un comando forcon una variabile di controllo che assume i valori da 0 al valore di a.length-1 (estremi inclusi).
Vedremo vari esempi di scansione di array con comandi for:


  • Trovare il massimo in un array
  • Ordinamento di un array
  • Copia (clone) di un array
Vedremo anche delle soluzioni alternative:
  • Trovare il massimo in un array, ricorsivamente
Per ognuno di questi problemi presenteremo un metodo statico della classe  ArrayUtils: sfrutteremo il fatto che gli array possono essere passati come parametri, e possono essere restituiti come risultato di un metodo (come già visto per i metodi fill_int e print).
I metodi della classe ArrayUtils sono stati scelti per illustrare come sia possibile:
  • Operare in modo iterativo e ricorsivo su array
  • Modificare il contenuto di array passati come parametri
  • Restituire un array come risultato




Trovare il massimo



La ricerca del massimo elemento di un dato insieme di valori è un'operazione piuttosto frequente, ad esempio nella gestione di strutture con priorità. Per questo motivo la classe ArrayUtils realizza un metodo (statico) che preso un array di interi ne individua e restituisce il massimo valore. Si noti che:

  • il metodo maxIterative ha un array di int come parametro;
  • i metodi non possono fare assunzioni sui valori passati come parametri, quindi devono considerare anche i casi limite, in questo caso l'array vuoto o con un solo elemento;
  • dovendo restituire comunque un valore intero, decidiamo in maniera arbitraria che il massimo di un array vuoto è la costante Integer.MIN_VALUE, cioè il minimo numero rappresentabile con un int (-2147483648). Nel secondo modulo vedremo soluzioni più eleganti e adeguate.


public class ArrayUtils { 

    // Restituisce il massimo di un array di interi,
    // Integer.MIN_VALUE se e' vuoto
    public static int maxIterative(int[] array){        
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < array.length; i++) 
            if (array[i] > max) max = array[i];
        return max;
    }
Per testare il metodo occorre scrivere una classe principale (cioè con il metodo main):


public class TestMaxIterative { 
    public static void main (String[] args) {
        int[] a = ArrayUtils.fill_int();
        int max = ArrayUtils.maxIterative(a);
        System.out.println("Il massimo e' " + max);

        /* Oppure, componendo in maniera funzionale:
        System.out.println("Il massimo e' " + 
                ArrayUtils.maxIterative(ArrayUtils.fill_int()) );
        */
    }
}





Massimo di un array, ricorsivamente


I prossimi metodi di ArrayUtils calcolano il massimo, ma usando la ricorsione invece di un for.
Il primo metodo, che è public, non fa altro che invocare un metodo ausiliario dichiarato private, quindi non invocabile all'esterno della classe.
Il metodo ausiliario ha due parametri: concettualmente maxRecursive(array, i) restituisce il massimo degli elementi dell'array a partire dalla posizione i. Assumendo che 0 <= i <= array.length, il metodo usa la seguente definizione ricorsiva:
  1. [caso base] se non ci sono elementi da analizzare (cioè i == array.length), allora maxRecursive(array,i) = Integer.MIN_VALUE;

  2. [caso ricorsivo] altrimenti maxRecursive(array,i) è il massimo tra array[i] e maxRecursive(array,i+1).




    public static int maxRecursive(int[] array) {
        return maxRecursive(array,0);
    }

    private static int maxRecursive(int[] array, int indice) {        
        if (indice >= array.length) 
            return Integer.MIN_VALUE;
        else
            return Math.max(array[indice], maxRecursive(array,indice+1));
    }






Ordinamento di un array


Il metodo sort di ArrayUtils ordina in modo crescente il contenuto dell'array passato per argomento, utilizzando due cicli  forannidati.


    public static void sort(int[] array) {
        for (int i = 0; i < array.length - 1; i++)
            for (int j = i+1; j < array.length; j++)
                if (array[i] > array[j]) {
                    int tmp = array[i];
                    array[i] = array[j];
                    array[j] = tmp;
                }
    }
L'algoritmo consiste di un ciclo annidato: si scorrono gli elementi dell'array confrontando l'elemento in posizione i con ciascuno degli elementi che lo seguono, scambiando i valori quando non sono ordinati.
Questo algoritmo di ordinamento è chiamato insertion sort e non è particolarmente efficiente. Ad esempio provate a pensare cosa succede se gli elementi sono inizialmente disposti in ordine inverso, dal più grande al più piccolo.
Esistono numerosi altri algoritmi di ordinamento più efficienti: ne vedrete alcuni a Fondamenti di Programmazione.
La cosa importante da notare è che gli scambi effettuati dal metodo si ripercuotono sull'array passato come argomento: questo è possibile perché il metodo conosce il riferimento all'oggetto-array.





Copia (clone) di un array


Il metodo clone di ArrayUtils restituisce una copia (clone) dell'array passato per argomento, cioè un array avente la stessa lunghezza e gli stessi elementi.
.


    public static int[] clone(int[] array) {
        int[] newArray = new int[array.length];
        for (int i = 0; i < array.length; i++)
            newArray[i] = array[i];
        return newArray;
    }
Si noti che poiché gli array sono oggetti, le variabili di tipo array contengono dei riferimenti. Quindi se si assegna una variabile di tipo array ad un'altra, si crea solo una copia del riferimento, non dell'array.


public class TestClone { 
    public static void main (String[] args) {
        int[] array = {3, 2, 5, 1, 4};
        int[] copia = array;        // copia solo il riferimento
        ArrayUtils.sort(copia);  // ordino
        System.out.print("Array copia: ");
        ArrayUtils.print(copia);    // 'copia' e' ordinato
        System.out.print("Array originale: ");       
        ArrayUtils.print(array);    // anche 'array' e' ordinato

        int[] array1 = {8, 7, 10, 6, 9};
        int[] copia1 = ArrayUtils.clone(array1); // copia
        ArrayUtils.sort(copia1); // ordino
        System.out.print("Array copia1: ");
        ArrayUtils.print(copia1);   // 'copia1' e' ordinato
        System.out.print("Array originale1: ");
        ArrayUtils.print(array1);   // ma 'array1' no
    }
}





Metodi su array di stringhe


La classe ArrayUtils realizza anche metodi statici per riempire e stampare array di stringhe. Il codice è analogo a quello che abbiamo visto per array di interi (ma la stampa avviene con un elemento per riga):




    public static String[] fill_String () {
        int n = 0;
        do {
            System.out.print("Dimensione array (intero positivo): ");
            n = Input.readInt();
        } while (n<=0);
        String[] array = new String[n];
        for (int i = 0; i < n; i++) {
            System.out.print("Dammi la stringa in posizione " + i + ": ");
            array[i] = Input.readLine();
        }
        return array;
    }

    public static void print (String[] array) {
        for (int i = 0; i < array.length; i++) 
            System.out.println(array[i]);
    }

Esempio: Adesso che finalmente abbiamo tutti gli elementi per interpretare il parametro args del metodo main possiamo scrivere facilmente un programma che stampa eventuali argomenti passati da linea di comando. Ad esempio:

   > java TestArgs uno due "t r e" ...




public class TestArgs { 
    public static void main (String[] args) {
        System.out.println("Stampa degli argomenti della linea di comando:");
        ArrayUtils.print(args);
   }
}
Notare che è possibile definire entrambi i metodi print (per array di interi e di stringhe) nello stesso file, perché il tipo del parametro usato in una generica invocazione permette di distinguere quale dei due metodi usare: uno si applica solo ad array di interi, l'altro solo ad array di stringhe.





Stampa con for generalizzato


Molti linguaggi mettono a disposizione un quarto costrutto iterativo, chiamato for-each, che permette di iterare sopra collezioni di dati (es. array, tipi enumerativi, insiemi, liste, ...) usando una sintassi particolarmente agevole e compatta.
Dalla versione 5.0 anche Java ha introdotto un costrutto simile, chiamato for generalizzato, con una sintassi diversa da quella usata in altri linguaggi (per non introdurre una nuova parole chiave creando problemi di backward compatibility). La sintassi è:
for ( <tipo> <nome_variabile> : <nome_array> )
    <corpo>
Questo comando è equivalente a:
for (int i = 0 ; i < <nome_array>.length ; i++) {
    <tipo> <nome_variabile> <nome_array>[i];
    <corpo>
}
Anche se il ciclo scorre tutti gli elementi dell'array, assegnandone il valore di volta in volta alla variabile <nome_variabile>, notate che la posizione del valore nell'array non è esplicitata nel codice del for generalizzato e quindi non è riferibile nel <corpo>del ciclo. Ne consegue che, in questo caso, il for generalizzato può essere usato unicamente per esaminare i valori contenuti nell'array, non per modificarne il contenuto.
Come esempi d'uso, vediamo due metodi: uno per stampare un array di stringhe e l'altro per calcolare la somma dei valori contenuti in un array di interi. Come esercizio, provate a confrontare il codice con quello che usa il ciclo for classico.


    public static void print (String[] array) {
        for (String s : array) 
            System.out.println(s);
    }


    public static int somma (int[] array) {
        int tot = 0;
        for (int v : array) 
            tot = tot + v;
        return tot;
    }




Teoria e pratica sui vettori e ArrayList


I Vettori

Breve cenno sulle classi generiche
Implementazione di vettori
Classe Vector e ArrayList: costruttori
Vector e ArrayList: metodi
Vettori e array a confronto
Array parzialmente riempiti
Vettori e tipi primitivi
Classi involucro
Esempio: la classe Integer
Esempio di uso di Vector<Integer>

http://www.di.unipi.it/~andrea/Didattica/LIP-07/Array/UsoArray/main.html

Commenti

Post popolari in questo blog

Simulazioni di reti (con Cisco Packet Tracer)

Esercizi sulla rappresentazione della virgola mobile IEEE 754 (Floating Point)