Beruflich Dokumente
Kultur Dokumente
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(); } }
dato
inizio (head)
Catena doppia
inizio (head) null null
prev next 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)
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 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
Non possiamo certo usare questo approccio per qualsiasi classe, deve esserci un metodo migliore!
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!
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
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
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);
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)
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
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
Come conseguenza, una variabile di tipo Object[ ] pu contenere un riferimento a un array che contenga oggetti di qualsiasi tipo
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();
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?
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 } ...
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.