I vettori


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). 

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). Ad esempio, per costruire un vettore di stringhe (inizialmente vuoto) e assegnarlo alla variabile vet, scriveremo:

      ArrayList<String> vet = new ArrayList<String>();

I vettori sono dunque simili agli array. Le differenze principali sono due:

  • la dimensione di un vettore può variare durante l'esecuzione di un programma;
  • il tipo base di un vettore NON può essere un tipo primitivo (int, double, ...).
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:

      String[] elementData = new String[initialCapacity];

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.

      String[] vettoreBis = new String[2*elementData.length]; 
      System.arraycopy(elementData,0,vettoreBis,0,elementData.length); 
      elementData = vettoreBis; 



Differenze tra Vector<E> e ArrayList<E>

Nel seguito, usando la sintassi Java 6.0, indicheremo con Vector<E> e ArrayList<E> le classi generiche che definiscono i vettori quando il tipo base non è specificato. Si noti che <E> può essere considerata una variabile di tipo, che può essere istanziata con qualunque classe Java.

Le classi generiche Vector<E> e ArrayList<E> sonosostanzialmente equivalenti, ma:

  • I metodi di Vector<E> sono sincronizzati, mentre quelli di ArrayList<E> non lo sono. Quindi se il programma è concorrente (cioè usa il multi-threading di Java) è opportuno usare Vector<E>, altrimenti convine sempre ArrayList<E> perché più efficiente.

  • Vector<E> fornisce, con opportuni metodi e costruttori, un controllo maggiore sulla capacità, cioè la dimensione dell'array soggiacente.

  • Per motivi storici, Vector<E> fornisce più metodi con nomi diversi per manipolare gli elementi di un vettore.

  • In genere conviene abituarsi ad usare sempre la classe ArrayList




Classi Vector<E> e ArrayList<E>: costruttori


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.

    /* crea un vettore vuoto, con i parametri specificati */
    Vector (int initialCapacity, int capacityIncrement)

    /* per default, capacityIncrement==0 */
    Vector (int initialCapacity)
    
    /* per default, initialCapacity==10 e capacityIncrement==0 */
    Vector ()

I costruttori di ArrayList<E> permettono di specificare solo la capacità iniziale del vettore.

    /* crea un vettore con la capacita' iniziale indicata */
    ArrayList (int initialCapacity)
    
    /* crea un vettore vuoto;
       la capacita' iniziale non e' specificata */
    ArrayList ()




Vector<E> e ArrayList<E>: metodi


I metodi che operano sui vettori consentono tra l'altro di:
  • leggere o sostituire l'elemento in una certa posizione (operazioni analoghe a quelle sugli array);

  • aggiungere uno o più elementi, in varie posizioni;
  • eliminare uno o più elementi, in varie posizioni;
  • cercare un oggetto nel vettore;
  • trasformare il vettore in un array.
Vediamo alcuni metodi di Vector<String> o ArrayList<String>:

    /* restituisce il numero di elementi contenuti nel vettore */
    int size ()
                                               
    /* restituisce l'elemento di indice index */
    String get (int index)
    
    /* sostituisce str all'oggetto di posizione index */
    String set (int  index, String str)
    
    /* inserisce str nella posizione index e sposta tutti gli
       elementi, da index in poi, di una posizione */
    void add (int index, String str)
    
    /* aggiunge str dopo l'ultimo elemento; restituisce true  */
    boolean add (String str)
                                               
    /* rimuove l'oggetto presente nella posizione index e sposta
       all'indietro di una posizione tutti gli elementi successivi
       a quello rimosso */
    void remove (int index)
                                               
    /* rimuove l'oggetto str se presente restituendo true,
       oppure restituisce false */
    boolean remove (String str)

    /* restituisce la prima posizione dell'oggetto 
       'elem' nel vettore, -1 se non esiste */
    int indexOf (String elem) 

    /* la stringa restituita ha la forma "[el-1, el-2, ..., el-n]" */
    String toString () 

    /* restituisce true se l'argomento è un vettore con 
    lo stesso numero di elementi (size), e gli elementi 
    corrispondenti sono equivalenti */
    boolean equals (Object vet) 

    /*  Solo in Vector<E>, non in ArrayList<E>: 
    restituisce la capacita' del vettore */
    int capacity ()

 

 

 
import java.util.ArrayList;
public class EsempioArrayList  {
    public static void main(String[] args) {
        ArrayList<String> v = new  ArrayList<String>(3);
        System.out.println("n.elementi di v: "+v.size());
        v.add("a");
        v.add("b");
        v.add("d");
        v.add("e");
        v.add(2,"c");
        System.out.println("n.elementidiv: "+v.size());
        for (int i=0; i < v.size(); i++)
            System.out.println("elemento "+ i+": "+v.get(i));
        String first = v.get(0);
        System.out.println("primo elemento di v: "+ first);
        System.out.println("ultimo elemento di v: "+v.get(v.size()-1));
        System.out.println(" toString: "+v);
    }
}
        
                              





Vettori e array a confronto


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.

// Nessun import richiesto import java.util.Vector; // oppure
import java.util.ArrayList;
String[] elenco = new String[5]; ArrayList<String> elenco =
    new ArrayList<String>();

for (int i=0 ; i<elenco.length ; i++){
  elenco[i] = "Numero "+i;
}
 

for (int i=0 ; i<5 ; i++) {
  elenco.add("Numero "+i);
}
 
System.out.println(elenco[2]); System.out.println(elenco.get(2));

for (int i=0 ; i<elenco.length ; i++){
  System.out.println(elenco[i]);
}
 

for (int i=0 ; i<elenco.size() ; i++){
  System.out.println(elenco.get(i));
}
 
// for generalizzato
for (String str : elenco){
  System.out.println(str);
}
 
// for generalizzato
for (String str : elenco){
  System.out.println(str);
}
 
elenco[2] = "Mario Rossi"; elenco.set(2,"Mario Rossi");

for (int i=3 ; i<elenco.length ; i++){
  elenco[i-1] = elenco[i];
}
elenco[elenco.length-1] = null

elenco.remove(2);

for (int i=elenco.length-1 ; i>2 ; i--){
  elenco[i] = elenco[i-1];
}
elenco[2] = "Mario Verdi";
 

elenco.add(2,"Mario Verdi");

 
 
 
 
 
 
 



Vettori e tipi primitivi


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:

    BigDecimal, BigInteger, Boolean, Byte, Character, Double, Float, Integer, Long, Number, Short
Ciascuna di queste classi contiene (oltre agli ovvi metodi toString() e equals()):
  • un costruttore con parametro di tipo primitivo (ad esempio Character(char c) o Integer(int value));
  • un costruttore con parametro String (ad esempio Integer(String s));
  • un metodo <tipo>Value che produce il valore di tipo primitivo corrispondente (ad esempio intValue());

  • eventuali metodi e costanti statiche di utilità generale.

 




Esempio: la classe Integer


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.

    Integer numero = 30;

    int count = numero;

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:

    Integer numero = new Integer(30);

    int count = numero.intValue();


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:

    int miliardo = Integer.parseInt("1000000000");

Un metodo analogo è il metodo parseDouble() della classe Double, che converte una stringa nel numero in virgola mobile che rappresenta.

Come vedremo, questi metodi lanciano un'eccezione se la stringa su cui sono invocati non rappresenta un numero.


Vediamo ora un semplice esempio di manipolazione di un vettore di interi. Si osservi che il tipo base del vettore è Integer.

import java.util.ArrayList;

public class ProvaArrayListInteger{
    public static void main(String [] args){

        ArrayList<Integer> vet = new ArrayList<Integer>();
        for (int i = 0; i < 5; i++){
            int n =i*i;
            vet.add(n);
        }
        System.out.println("Ecco i 5 numeri inseriti");
        for (int num : vet)
            System.out.println(num);
   }
}

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.  




Uso dell'interfaccia Iterable<T>


L'interfaccia Iterable<T> deve essere implementata dalle collezioni che possono essere scandite con il for semplificato. Queste non sono ovviamente ristrette alle classi delle Api di Java ma possiamo farci le nostre

import java.util.*;

public class IterableTests implements Iterable<Integer> {

     ArrayList<Integer> vet = new ArrayList<Integer>();

     public IterableTest(){
        for (int i = 0; i < 5; i++){
            int n =i*i;
            vet.add(n);
        }
     }

     public Iterator<Integer> iterator(){
        return  vet.iterator();
     }
}

 
public class Prova{

   public static void main(String [] args){
        IterableTest ii = new IterableTest();
        for (int num : ii) System.out.println(num);
   }
}




Riassumendo


Si usa
preferibilmente quando
un array
  • I dati sono tipi elementari;

  • La lunghezza dell'array è nota al momento della creazione;

  • La lunghezza dell'array non varia dinamicamente.

ArrayList
  • I dati non sono tipi elementari o si usa un tipo involucro;

  • La lunghezza non è nota al momento della creazione;

  • La lunghezza varia dinamicamente;

Vector
 

      Solo per compatibilità con programmi vecchi.