Esercitazione su Vector
Permette di definire collezioni di dati generiche, che sono in grado di memorizzare elementi di ogni sottotipo di Object Definito nella classe java.util.Vector -memorizzano sequenze di oggetti di lunghezza variabile -possono memorizzare oggetti di tipo diverso, purche’ sottotipi di Object, (es. String, Integer etc.), anche tra loro non omogenei
Specifica (alcuni metodi) public Vector (){ \\EFFECTS\\EFFECTS: crea un vettore vuoto} Notate che a differenza che per gli arrays non e’ necessario fissare al momento della creazione la dimensione Ci sono anche altri costruttori tipo quelli degli arrays che permettono di creare un vettore vuoto ma con una certa capacita’ (dato numero di posizioni allocate ma vuote). Serve solo per avere implementazioni piu’ o meno efficienti (per ora lo ignoriamo)
public int size (){ \\EFFECTS\\EFFECTS: restituisce il numero di elementi presenti nel vettore} public Object elementAt (int index){ \\EFFECTS\\EFFECTS: restituisce l'elemento di indice index } public void setElementAt (Object obj, int index){ \\EFFECTS\\EFFECTS: sostituisce obj all'oggetto della posizione index} Se index e’ fuori dal size del vettore viene sollevata una eccezione come per gli arrays Metodi simili a quelli dell’array
public void insertElementAt (Object obj, int index){ \\MODIFIES:this \\EFFECTS: inserisce obj nella posizione index e sposta tutti gli\\EFFECTS elementi, da index in poi, di una posizione} public void addElement (Object obj){ \\MODIFIES:this \\EFFECTS\\EFFECTS: aggiunge una posizione alla fine che contiene obj } La dimensione del Vector cambia, viene aggiunta una posizione alla fine o in un dato punto Metodi per aggiungere
public void removeElementAt (int index){ \\MODIFIES:this \\EFFECTS: rimuove l'oggetto presente nella posizione index e sposta all'indietro di una posizione tutti gli elementi successivi a quello rimosso} public boolean removeElement (Object obj){ \\MODIFIES:this \\EFFECTS: rimuove la prima occorrenza dell'oggetto obj se presente restituendo true,oppure restituisce false} La dimensione del Vector cambia, viene ricompattato (non rimane una posizione vuota) Metodi per rimuovere
Esercizio Proposto Definire una classe VectorOp che definisce alcune procedure stand-alone (metodi statici) che operano su Vector contenenti Integer Il fatto che un Vector contenga Integer non e’ intrinseco nel tipo di dato (va messa una precondizione)
public class VectorOp{ \\OVERVIEW\\OVERVIEW: fornisce procedure stand-alone per manipolare Vectors di Integer public static int min(Vector v){ \\REQUIRES\\REQUIRES: v non e’ null e contiene Integer \\EFFECTS\\EFFECTS: restituisce il minimo di v, 0 se e’ vuoto if (v.size()==0) {return 0;} int min=((Integer) v.elementAt(0)).intValue(); for (int i=1; i < v.size(); i++) {int x= ((Integer) v.elementAt(i)).intValue(); if (x < min) {min=x;} } return min; } NOTA: uso del Cast!
public static boolean search(Vector v,int x){ \\REQUIRES: v non e’ null e contiene Integer \\EFFECTS\\EFFECTS: restituisce true se x appartiene a v, false altrimenti for (int j=0; j < v.size(); j++) {int y=((Integer) v.elementAt(j)).intValue(); if (x==y) {return true;} } return false;} public static Vector inc(Vector v,int x){ \\REQUIRES: v non e’ null e contiene Integer \\EFFECTS\\EFFECTS: restituisce il Vector ottenuto incrementando ogni elemento di x Vector nuovo=new Vector(); for (int j=0; j < v.size(); j++) {int y= ((Integer) v.elementAt(j)).intValue(); nuovo.addElement(new Integer(y+x) );} return nuovo;}
public static Vector reverse(Vector v){ \\REQUIRES: v non e’ null e contiene Integer \\EFFECTS\\EFFECTS: restituisce il Vector ottenuto invertendo gli elementi di v Vector nuovo=new Vector(); for (int j=v.size()-1; j >=0; j--) { nuovo.addElement( v.elementAt(j) );} \\ cast non necessario return nuovo; } public static void remove(Vector v,int x){ \\REQUIRES: v non e’ null e contiene Integer \\MODIFIES\\MODIFIES: v \\EFFECTS\\EFFECTS: elimina tutte le occorrenze di x in v for (int j=0; j < v.size(); j++) {int y= ((Integer) v.elementAt(j)).intValue(); if (y==x) v.removeElementAt(j );} }
public static Vector sort(Vector v){ \\REQUIRES: v non e’ null e contiene Integer \\EFFECTS\\EFFECTS: restituisce il Vector ottenuto mettendo gli elementi di v in ordine \\ crescente Vector nuovo=new Vector(); \\ vettore che viene mantenuto ordinato if (v.size()==0) {return nuovo;} for (int i=0; i< v.size();i++){ int y=((Integer) v.elementAt(i)).intValue(); \\ elemento di v da inserire boolean trovato=false; int j=0; while (j < nuovo.size() && !trovato) \\ scorre nuovo cercando il punto giusto {int w=((Integer) nuovo.elementAt(j)).intValue(); if (w >= y) {nuovo.insertElementAt(new Integer(y),j); trovato=true;} else j++; } if (! trovato) {nuovo.addElement(new Integer(y));} } return nuovo;}
Commenti Quando usare Vector o Array? Array piu’ efficienti Vector utile quando bisogna implementare strutture dati di lunghezza variabile Esempio: pila (Stack) di interi Mantiene gli elementi per ordine di inserimento (LIFO)
Quali operazioni? isEmpty() serve per testare se la pila e’ vuota top() serve per leggere l’elemento al top della pila, ovvero l’ultimo inserito pop() rimuove l’ultimo elemento inserito (al top della pila) push (int x) inserisce x nella pila al top
Specifica (INTERFACCIA PUBBLICA) public class Stack { \\ OVERVIEW: uno Stack e’ una collezione di interi organizzati per ordine di inserimento con una politica LIFO. E’ modificabile public Stack () { \\ EFFECTS: costruisce uno Stack Vuoto } public boolean isEmpty() { \\ EFFECTS: se this e’ vuoto restituisce true, altrimenti false } public int top() { \\ REQUIRES: this non e’ vuoto \\ EFFECTS: restituisce l’ultimo elemento inserito } NOTA: specifica parziale (eccezioni?)
Modificatori public void pop() { \\ REQUIRES: this non e’ vuoto \\ MODIFIES: this \\ EFFECTS: se this non e’ vuoto rimuove l’ultimo elemento inserito} public void push (int x) { \\ MODIFIES: this \\ EFFECTS: inserisce x nella pila (al top) }
Implementazione Rappresentazione: usiamo un Vector per implementare la pila, l’ultimo elemento inserito e’ l’ultimo elemento (top della pila) Chiaramente dovremo usare un Vector di Integer Implementiamo i metodi di conseguenza Nota: l’implementazione deve essere nascosta (usiamo variabili private)
Implementazione public class Stack { \\ OVERVIEW: uno Stack e’ una collezione di interi organizzati per ordine di inserimento con una politica LIFO. E’ modificabile private Vector pila; \\ rappresentazione privata public Stack () { \\ EFFECTS: costruisce uno Stack vuoto pila=new Vector();} public boolean isEmpty() { \\ EFFECTS: se this e’ vuoto restituisce true, altrimenti false if (pila.size()==0) {return true;}; return false; }
public int top() {\\REQUIRES: this non e’ vuoto \\ EFFECTS: restituisce l’ultimo elemento inserito Integer x=pila.elementAt(pila.size()-1)); return x.intValue(); } public void pop() { {\\REQUIRES: this non e’ vuoto \\ MODIFIES: this \\ EFFECTS: rimuove l’ultimo elemento inserito pila.removeElementAt(pila.size()-1));} public void push (int x) { \\ MODIFIES: this \\ EFFECTS: inserisce x nella pila pila.addElement(new Integer(x));} }
Interfaccia Pubblica Ricordiamo che: tutto il codice che usa Stack deve essere scritto usando solo interfaccia pubblica (non vede che e’ implementata con un Vector) Esempio: procedura stand-alone che rimpiazza gli ultimi due elementi inseriti con la loro somma
public static void sum (Stack p) { \\REQUIRES: p contiene almeno due elementi \\MODIFIES\\MODIFIES: p \\EFFECTS\\EFFECTS: rimpiazza gli ultimi due elementi inseriti in p con la loro somma int primo, secondo; primo = p.top(); p.pop(); secondo = p.top(); p.pop(); p.push(primo+secondo); } Notate che non accede alla variabile pila di tipo Vector, usa solo l’interfaccia pubblica!
Commenti Sarebbe piu’ facile scrivere il codice accedendo direttamente alla rappresentazione del tipo di dato, e.g. private Vector pila; //la rappresentazione ESEMPIO Integer primo, secondo; primo = (Integer) p.pila.elementAt(p.pila.size()-1); secondo= (Integer) p.pila. elementAt(p.pila.size()-2); p.pila.removeElementAt(p.pila.size()-1); p.pila.setElementAt(primo.intValue()+secondo.intValue(),p.pila.size(0-1);
Ma…. Questo approccio e’ metodologicamente sbagliato Mantenere l’implementazione nascosta permette di -garantire proprieta’ del tipo di dato -rendere il codice che lo usa indipendente dalla sua implementazione
Esempio Se il tipo di dato dovesse essere riprogettato (lo implemento p.e. con una lista concatenata invece che con un Vector) Tutto il codice scritto usando l’interfaccia pubblica continuerebbe a funzionare Al contrario se accedesse alla rappresentazione dovrebbe essere riprogettato
Esercizio Proposto Definire una classe Abbonato i cui oggetti descrivono le informazioni relative ad un abbonato al telefono: nome (di tipo String ) e numero di telefono (int) Con i seguenti costruttori e metodi
public class Abbonato{ public Abbonato(String s,int n){ \\EFFECTS: crea un nuovo abbonato con nome s\\EFFECTS \\ e numero n} public String nome(){ \\EFFECTS\\EFFECTS: restituisce il nome di this} public int numero(){ \\EFFECTS\\EFFECTS: restituisce il numero di this} \\ metodo overridden public boolean equals(Object o){ \\REQUIRES\\REQUIRES: o e’ di tipo Abbonato \\EFFECTS\\EFFECTS: restituisce true se this ed o sono uguali} }
Definire una classe Elenco i cui elementi sono di tipo Abbonato Gli Abbonati sono mantenuti all’interno dell’Elenco in modo ordinato rispetto al nome, ESEMPIO: (Francesca, 2867) <= (Marco, 133) Inoltre i numeri di telefono nell’elenco sono tutti diversi Per Elenco vengono forniti metodi per -cercare all’interno dell’elenco il numero di telefono a partire dal nome - inserire un nuovo Abbonato - rimuovere un Abbonato - cambiare il numero di telefono
public class Elenco{ public Elenco(){ \\EFFECTS: crea un nuovo elenco vuoto}\\EFFECTS public int cercanum(String s) { \\EFFECTS\\EFFECTS: restituisce il numero di telefono del primo abbonato presente in this che ha nome s, altrimenti restituisce 0} public void inserisci(String s){ \\MODIFIES: this\\MODIFIES \\EFFECTS: aggiunge un nuovo Abbonato all’elenco (this)\\EFFECTS con nome s e con un nuovo numero di telefono}
public void rimuovi(String s){ \\MODIFIES: this\\MODIFIES \\EFFECTS: rimuove dall’elenco (this) tutti gli abbonati\\EFFECTS con nome s} public int cambia(String s, int x){ \\MODIFIES: this\\MODIFIES \\EFFECTS: se nell’elenco compare l’abbonato (s,x),\\EFFECTS rimpiazza il numero x con un nuovo numero di telefono y e lo restituisce } public String toString(){ \\EFFECTS\\EFFECTS: restituisce una stringa che contiene la sequenza di \\abbonati di this }
Implementazione: suggerimenti Nella classe Elenco usare una variabile statica per memorizzare il primo numero di telefono disponibile (inizializzata inizialmente ad 1), tipo come abbiamo fatto in classe per gestire il numero di matricola una variabile d’istanza di tipo Vector per memorizzare gli abbonati (sfruttando il fatto che Abbonato e’ sottotipo di Object) Attenzione: l’ordinamento va mantenuto (il Vector e’ ordinato) e sfruttato (p.e. per la ricerca)
Al solito In parallelo il testing delle due classi Usa solo l’interfaccia pubblica