Sie sind auf Seite 1von 35

Coda doppia realizzata con una catena

La realizzazione di una coda doppia (deque) mediante una catena davvero naturale: i metodi hanno gli stessi nomi Il comportamento funzionale della struttura dati catena perfettamente simmetrico, proprio come quello dellADT coda doppia Le prestazioni non sono simmetriche, ma vedremo poi come fare
public class LinkedListDeque<T> implements Deque<T> { private java.util.LinkedList<T> list = new java.util.LinkedList<T>(); public int size() { return list.size(); } public boolean isEmpty(){ return list.isEmpty(); } public void addFirst(T obj) { list.addFirst(obj); } public void addLast(T obj) { list.addLast(obj); } public T getFirst() { return list.getFirst(); } public T getLast() { return list.getLast(); } public T removeFirst() { return list.removeFirst(); } // QUESTO METODO O(n) public T removeLast() { return list.removeLast(); } }

Catena doppiamente concatenata


Per rendere simmetriche anche le prestazioni, necessario poter navigare nella struttura concatenata nei due sensi (catena doppia o catena doppiamente concatenata) Ogni nodo deve poter accedere direttamente al nodo precedente, oltre che al nodo successivo La catena doppia deve usare nodi pi complicati, ciascuno dei quali contenga
prev next un riferimento a un dato un riferimento al nodo successivo della lista (next) un riferimento al nodo precedente della lista (prev)

dato

inizio (head)

fine (tail) null

Catena doppia
inizio (head) null null
prev next prev next

null next null null Catena doppia vuota fine (tail)


prev next

prev

null

null

dato

dato

Dato che la struttura funzionalmente simmetrica, si usano solitamente due nodi che non contengono dati, uno a ciascun estremo della catena (detti header e trailer)

Nodo di una catena doppia


class DLLNode<T> // sta per DoubleLinkedListNode { private T element; private DLLNode<T> next, prev; public DLLNode(T e, DLLNode<T> n, DLLNode<T> p) { element = e; next = n; prev = p; } public DLLNode() { this(null, null, null); } public T getElement() { return element; } public DLLNode<T> getNext() { return next; } public DLLNode<T> getPrev() { return prev; } public void setElement(T e) { element = e; } public void setNext(DLLNode<T> n) { next = n; } public void setPrev(DLLNode<T> p) { prev = p; } }

Catena doppia
Tutto quanto detto per la catena (semplice) pu essere agevolmente esteso alla catena doppia, ma il metodo removeLast diventa O(1) come gli altri metodi La coda doppia realizzata mediante catena doppia ha prestazioni ottimali e simmetriche
Usare, invece, una catena doppia per realizzare una coda o una pila uno spreco di spazio di memoria... anche se ovviamente possibile

Catena: ispezione totale?


Per effettuare l'ispezione totale dei dati presenti in una catena (senza "distruggerla") si usa uno strumento simile allo Scanner o allo StringTokenizer e chiamato Iterator ("iteratore") Un oggetto, associato alla catena (semplice o doppia), che, mediante ripetute invocazioni del suo metodo next(), restituisce ordinatamente i riferimenti ai dati contenuti nella catena stessa, dall'inizio alla fine L'iteratore ha anche il metodo hasNext(), che consente di sapere se la scansione della catena terminata Per ottenere un iteratore, bisogna chiederlo alla catena! un argomento molto pi complesso (Dati e Algoritmi 1) LinkedList<String> list = new LinkedList<String>(); ... // inserimento dati nella catena Iterator<String> iter = list.iterator(); while (iter.hasNext()) System.out.println(iter.next());

Catena o array?
Dal punto di vista delle prestazioni temporali, le due soluzioni che abbiamo visto per implementare, ad esempio, una coda sembrano essere equivalenti e, asintoticamente, lo sono
Con catena: tutte le operazioni sono O(1) Con array: loperazione enqueue mediamente O(1) (ma occasionalmente O(n)), mentre tutte le altre operazioni sono O(1) La differenza sta tutta nel mediamente

Catena o array?
Esempio: un sistema di gestione della posta elettronica via web accetta nuovi messaggi inviati da un utente inserendoli in una coda, dalla quale vengono poi effettivamente spediti

Nel momento in cui il messaggio viene inserito in coda, il server web risponde allutente con la pagina di messaggio inviato
in realt il messaggio stato inviato a un server di posta che lo invier realmente al destinatario comportamento indispensabile perch il server di posta del destinatario potrebbe essere spento, irraggiungibile o sovraccarico, mentre il server di posta del mittente potrebbe essere a sua volta oberato da una lunga coda di messaggi in attesa di essere spediti

molto importante che il mittente non debba attendere troppo tempo prima di poter uscire dal sistema

Catena o array?
Alcune ipotesi (sono tempi inventati)
Tempo di inserimento del messaggio in una coda realizzata con array 1 s = 10-6 s Tempo di inserimento del messaggio in una coda realizzata con catena 2 s = 210-6 s 0.1 s = 10-7 s Tempo necessario per il raddoppio della dimensione di un array contenente 100 milioni di messaggi = 10810-7 s = 10 s ( ragionevole che sia un po pi lento) Tempo di copiatura di una cella di array

Usando una coda realizzata con array, OGNI TANTO un utente deve attendere 10 secondi, invece di ricevere la pagina quasi istantaneamente la cosa sostanzialmente inaccettabile

Ordinamento e ricerca

di oggetti

Ordinamento di oggetti
Abbiamo visto algoritmi di ordinamento efficienti e ne abbiamo verificato il funzionamento con array di numeri o di stringhe, ma spesso si pone il problema di ordinare dati pi complessi ad esempio, ordinare conti bancari in generale, ordinare oggetti Vedremo ora che si possono usare gli stessi algoritmi, a patto che gli oggetti da ordinare siano tra loro confrontabili

Ordinamento di oggetti
Per ordinare numeri con gli algoritmi che abbiamo visto necessario effettuare confronti
Esistono algoritmi di ordinamento che non fanno confronti (corso di Dati e Algoritmi 1)

La stessa affermazione vera per oggetti per ordinare oggetti con gli algoritmi che conosciamo necessario effettuare confronti tra loro C per una differenza sostanziale confrontare numeri ha un significato ben definito dalla matematica confrontare oggetti ha un significato che dipende dal tipo di oggetto e a volte pu non avere alcun significato

Ordinamento di oggetti
Confrontare oggetti ha un significato che dipende dal tipo di oggetto Quindi sensato che una classe definisca anche il significato del confronto tra i suoi esemplari, se un confronto possibile Ad esempio, la classe String definisce il metodo compareTo che attribuisce un significato ben preciso allordinamento tra stringhe: lordinamento lessicografico Abbiamo potuto, quindi, riscrivere i metodi di ordinamento (e ricerca) in modo che manipolino stringhe invece di numeri, senza cambiare gli algoritmi

Ordinamento di oggetti
Abbiamo riscritto i metodi di ordinamento e ricerca in modo che manipolino stringhe invece di numeri, senza cambiare gli algoritmi

Ma come fare per altre classi?


possiamo ordinare oggetti di tipo BankAccount in ordine, ad esempio, di saldo crescente? bisogna definire un metodo compareTo nella classe BankAccount bisogna riscrivere i metodi perch accettino come parametro un array di BankAccount

Non possiamo certo usare questo approccio per qualsiasi classe, deve esserci un metodo migliore!

Idea: Object definisce compareTo


Si potrebbe pensare che il metodo compareTo sia stato definito in Object (e sovrascritto ad esempio in String), proprio come equals
public class Object { public int compareTo(Object obj) { return ... } // invece non c!! ... }

Idea: Object definisce compareTo


Per far funzionare questo metodo dovremmo definire una relazione dordine completa nellinsieme degli oggetti Java, basata sulle sole (pochissime) propriet degli esemplari di Object Idea: in analogia a quanto avviene nel metodo equals, diciamo che un oggetto minore di un altro se il suo indirizzo in memoria inferiore public int compareTo(Object obj) { if (this < obj) return -1; if (this > obj) return 1; return 0; } // o semplicemente return (this obj)

Idea: Object definisce compareTo


public int compareTo(Object obj) { if (this < obj) return -1; if (this > obj) return 1; return 0; } // o semplicemente return (this obj)

Non funziona, perch (diversamente da altri linguaggi) Java non consente il confronto di relazione tra riferimenti (solo == e !=) lesecuzione di operazioni aritmetiche tra riferimenti
logico: dato che non possibile decidere in quale posizione di memoria viene creato un oggetto, non ha senso scrivere codice che dipenda dalla posizione relativa di due oggetti (che sar sostanzialmente casuale dal punto di vista del programmatore), quindi non vengono dati strumenti per verificare questa relazione!

Realizzare una propriet astratta


Quello che deve fare una classe perch i suoi esemplari possano essere ordinati , ad esempio
definire un metodo adatto a confrontare esemplari della classe, con lo stesso comportamento di compareTo

Gli oggetti della classe diventano confrontabili


gli algoritmi di ordinamento e ricerca non hanno bisogno di conoscere alcun particolare degli oggetti sufficiente che gli oggetti siano tra loro confrontabili

Per definire un comportamento astratto si usa in Java la definizione di una interfaccia, che deve essere realizzata (implementata) da una classe che dichiari di avere tale comportamento

Realizzare una propriet astratta


public interface Comparable { int compareTo(Object other); } definita in java.lang
public class BankAccount implements Comparable { ... public int compareTo(Object other) { // notare che viene sempre ricevuto un Object // quindi serve una conversione con down-casting return (balance ((BankAccount) other).balance); } }

Si dovrebbe proteggere il down-cast con instanceof o try/catch ma, secondo le sue specifiche, in caso di tipo errato il metodo deve lanciare ClassCastException che viene gi lanciata! Quindi basta lasciarla andare

Linterfaccia Comparable
public interface Comparable<T> { int compareTo(T other); }

Linterfaccia Comparable definita nel pacchetto java.lang, per cui non c bisogno di importarla

In realt, si tratta di uninterfaccia generica


la classe String, ad esempio, realizza Comparable<String> , perch confrontare stringhe con oggetti che non siano stringhe non ha senso in questo modo il codice del metodo compareTo si semplifica, non serve pi il down-cast

Lutilizzo di tale interfaccia nella sua forma generica richiede una caratterizzazione avanzata dei tipi generici, che non faremo

Linterfaccia Comparable
Useremo linterfaccia Comparable nella sua forma non generica public interface Comparable
{ } int compareTo(Object other);

Il compilatore generer i consueti warning


Note: Hello.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.

Come pu linterfaccia Comparable risolvere il nostro problema di definire un metodo di ordinamento valido per tutte le classi?
Basta definire un metodo di ordinamento che ordini un array di riferimenti a oggetti che realizzano linterfaccia Comparable, indipendentemente dal tipo effettivo degli oggetti

public class ArrayAlgorithms { public static void selectionSort(Comparable[] a) { for (int i = 0; i < a.length - 1; i++) { int minPos = findMinPos(a, i); if (minPos != i) swap(a, minPos, i); } } private static void swap(Comparable[] a, int i, int j) { Comparable temp = a[i]; a[i] = a[j]; a[j] = temp; } private static int findMinPos(Comparable[] a, int from) { int pos = from; for (int i = from + 1; i < a.length; i++) if (a[i].compareTo(a[pos]) < 0) pos = i; return pos; } ... }

Ordinamento di oggetti
Definito un algoritmo per ordinare un array di riferimenti Comparable, se vogliamo ordinare un array di esemplari di BankAccount basta fare
BankAccount[] v = new BankAccount[10]; // creazione dei singoli elementi dellarray ... // eventuali modifiche allo stato // degli oggetti dellarray ... ArrayAlgorithms.selectionSort(v);

Dato che BankAccount realizza Comparable, larray di riferimenti viene convertito con up-casting

Ordinamento di oggetti
Tutti i metodi di ordinamento e ricerca che abbiamo visto per array di numeri interi possono essere riscritti per array di oggetti Comparable, usando le seguenti traduzioni
if (a < b) if (a > b) if (a == b) if (a != b) if (a.compareTo(b) < 0) if (a.compareTo(b) > 0) if (a.compareTo(b) == 0) if (a.compareTo(b) != 0)

QUESTI METODI NON SI POSSONO USARE ALLESAME

Ordinamento e ricerche di libreria


La libreria standard fornisce, per lordinamento e la ricerca, alcuni metodi statici in java.util.Arrays
un metodo sort (sovraccarico) che ordina (sul posto) array di tutti i tipi fondamentali e array di Comparable String[] ar = new String[10];
... Arrays.sort(ar);

un metodo binarySearch (sovraccarico) che cerca un elemento in array di tutti i tipi fondamentali e in array di Comparable restituisce la posizione come numero intero, restituisce -1 se non trova quanto cercato

Up-casting e down-casting con array


Materiale utile solo per i pi "curiosi" Soltanto le informazioni relative agli array in codice generico sono per tutti

Conversione tra array


Ricordiamo che (up-casting)
Un riferimento a un oggetto di una sottoclasse pu essere assegnato a una variabile riferimento che sia del tipo di una sua superclasse (diretta o indiretta) Gli array sono oggetti primitivi, non sono esemplari di una classe
Cosa possiamo dire sulla conversione tra riferimenti ad array?

Conversione tra array


Innanzitutto sappiamo che un array un oggetto
Nuova regola sintattica: Un riferimento a un array di qualsiasi tipo pu essere assegnato a una variabile di tipo Object, come un riferimento a qualsiasi altro oggetto in Java String[] ar = new String[10]; Object obj = ar; // up-casting ... String[] xx = (String[]) obj; // down-casting Questo anche se NON si pu dire che Object sia una superclasse della classe array, dato che questultima non esiste

Conversione tra array


Nuova regola sintattica: Un riferimento a un array di esemplari di una sottoclasse pu essere assegnato a un riferimento ad array di esemplari di una sua superclasse (diretta o indiretta) String[] ar = new String[10]; Object[] objs = ar; // String deriva da Object
La semantica un po diversa dallup-casting tra oggetti che non siano array: non ha molto senso dire manipoliamo larray di stringhe tramite una finestra pi piccola, con minori funzionalit

Quello che si fa usare una finestra pi piccola per ciascuna cella dellarray, perch un riferimento a Object una restrizione di un riferimento a String

Conversione tra array


Esempi
SavingsAccount[] sa = new SavingsAccount[2]; BankAccount[] ba = sa; BankAccount x = ba[0]; x.deposit(500); SavingsAccount s = (SavingsAccount) ba[0]; s.addInterest(); ... SavingsAccount[] sa2 = (SavingsAccount[]) ba; SavingsAccount s2 = sa2[0];

Come conseguenza, una variabile di tipo Object[ ] pu contenere un riferimento a un array che contenga oggetti di qualsiasi tipo

Conversione tra array


Attenzione: i tipi primitivi non sono oggetti!
double[] dd = new double[10]; Object[] objs = dd; // errore di compilazione // perch un valore di tipo double non pu // essere assegnato a una variabile di tipo // Object, quindi la stessa conversione NON // pu avvenire tra array corrispondenti

Per gli array di tipi primitivi sono oggetti!


double[] dd = new double[10]; Object obj = dd; ... double[] dd2 = (double[]) obj;

Conversione tra array


Una cosa un po strana cosa succede qui?
SavingsAccount[] sa = new SavingsAccount[1]; BankAccount[] ba = sa; ba[0] = new BankAccount();

Il compilatore protesta? E linterprete? Perch? Cosa succederebbe qui se linterprete non protestasse nella riga precedente? Quali oggetti sono presenti?
SavingsAccount[] sa = new SavingsAccount[1]; BankAccount[] ba = sa; ba[0] = new BankAccount(); sa[0].addInterest();

Sarebbe meglio che il compilatore protestasse?

Array in codice generico


Non si possono creare array di tipo generico
public class ArrayList<T> { private T[] a; private int aSize; public ArrayList() { a = new T[1]; // non compila! ...

Altre cose che non si possono fare new T() ... instanceof T Perch? Argomento complesso i pi curiosi possono cercare Java raw types e Java type erasure Come facciamo a progettare una classe generica come ArrayList, che usi un array per memorizzare dati?

Array in codice generico


Prima soluzione: usare come variabile di esemplare un array di (riferimenti di tipo) Object
I metodi di inserimento (come add) ricevono un riferimento di tipo T, che pu essere assegnato a una cella di tale array mediante up-casting (implicito) I metodi di ispezione/rimozione, che restituiscono un riferimento di tipo T, devono usare il down-casting (senza bisogno di controllo, per la semantica dei metodi di inserimento che garantiscono la presenza di soli riferimenti di tipo T nellarray)

public class ArrayList<T> { private Object[] a; private int aSize; public ArrayList() { aSize = 0; a = new Object[1]; } public void add(T element) { ... // se non c spazio, ingrandisco... a[aSize++] = element; // up-casting } public T get(int index) { ... // index valido? return (T) a[index]; // down-casting } ...

Array in codice generico


Seconda soluzione: usare come variabile di esemplare un array di (riferimenti di tipo generico) T, ma creare array di (riferimenti a) Object e usare un down-casting! (che in realt un po strano)
public class ArrayList<T> { private T[] a; private int aSize; public ArrayList() { a = (T[]) new Object[1]; // il compilatore emette warning ... I pi curiosi possono cercare Java raw types e Java type erasure

Loperazione avviene sotto il controllo del programmatore, che ha la responsabilit di NON memorizzare nellarray di Object riferimenti a oggetti che NON siano di tipo T (o di una sottoclasse di T) La condizione agevolmente rispettata se larray viene assegnato a una variabile di tipo T[] subito dopo essere stato creato, prima di poter assegnare riferimenti alle sue celle Infatti, dopo tale assegnamento, con la variabile a di tipo T[] non possibile usare larray in modo scorretto

Il compilatore segnala:
Note: Hello.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.

Das könnte Ihnen auch gefallen