Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... ·...
Transcript of Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... ·...
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 1
1
Strutture dati
2
Tipi di dati astratti✔ Una struttura dati (data structure) è un modo
sistematico di organizzare i dati in un contenitoree di controllarne le modalità d’accesso– in Java si definisce una struttura dati con una classe
✔ Un tipo di dati astratto (ADT, Abstract DataType) è una rappresentazione astratta di unastruttura dati, un modello che specifica:– il tipo di dati memorizzati– le operazioni che si possono eseguire sui dati– il tipo delle informazioni necessarie per eseguire le
operazioni
3
Tipi di dati astratti✔ In Java si definisce un tipo di dati astratto con una
interfaccia✔ Come sappiamo, un’interfaccia descrive un
comportamento che sarà assunto da una classe cherealizza l’interfaccia– è proprio quello che serve per definire un ADT
✔ Un ADT definisce cosa si può fare con unastruttura dati che realizza l’interfaccia– la classe che rappresenta concretamente la struttura dati
definisce invece come vengono eseguite le operazioni
4
Il pacchetto java.util✔ Il pacchetto java.util della libreria standard
contiene molte definizioni di ADT comeinterfacce e loro realizzazioni come classi
✔ La nomenclatura e le convenzioni usate in questopacchetto sono, però, piuttosto diverse da quelletradizionalmente utilizzate nella teoriadell’informazione (purtroppo e stranamente...)
✔ Quindi, proporremo un’esposizione teorica diADT usando la terminologia tradizionale, senzausare il pacchetto java.util della libreria standard
5
Tipi di dati astratti✔ Un tipo di dati astratto mette in generale a
disposizione metodi per svolgere le seguentiazioni– inserimento di un elemento– rimozione di un elemento– ispezione degli elementi contenuti nella struttura
• ricerca di un elemento all’interno della struttura
✔ I diversi ADT che vedremo si differenziano per lemodalità di funzionamento di queste tre azioni
6
Pila (stack)
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 2
7
Pila (stack)✔ In una pila (stack) gli oggetti possono essere
inseriti ed estratti secondo un comportamentodefinito LIFO (Last In, First Out)– l’ultimo oggetto inserito è il primo ad essere estratto– il nome è stato scelto in analogia con una pila di piatti
✔ L’unico oggetto che può essere ispezionato èquello che si trova in cima alla pila
✔ Esistono molti possibili utilizzi di una strutturadati con questo comportamento– la JVM usa una pila per memorizzare l’elenco dei
metodi in attesa durante l’esecuzione in un dato istante
8
Pila (stack)✔ I metodi che caratterizzano una pila sono
– push per inserire un oggetto in cima alla pila– pop per eliminare l’oggetto che si trova in cima alla pila– top per ispezionare l’oggetto che si trova in cima alla
pila, senza estrarlo✔ Esiste anche il metodo (non indispensabile)
– topAndPop che combina gli effetti di top e di pop✔ Infine, ogni ADT di tipo “contenitore” ha i metodi
– isEmpty per sapere se il contenitore è vuoto– makeEmpty per vuotare il contenitore
9
Contenitore generico
✔ Anche le interfacce, come le classi, possono essere“estese”
✔ Un’interfaccia eredita tutti i metodi della suasuper-interfaccia
✔ Per realizzare un’interfaccia estesa occorredefinire anche i metodi della sua super-interfaccia
public interface Container{ boolean isEmpty(); void makeEmpty();}
public interface Stack extends Container{ ...}
10
✔ Definiremo tutti gli ADT in modo che possanogenericamente contenere oggetti di tipo Object– in questo modo potremo inserire nel contenitore oggetti
di qualsiasi tipo, perché qualsiasi oggetto può essereassegnato ad un riferimento di tipo Object
Pila (stack)public interface Stack extends Container{ void push(Object obj); void pop(); Object top(); Object topAndPop();}
11
Utilizzo della pila✔ Per evidenziare la potenza della definizione di tipi
di dati astratti come interfacce, supponiamo chequalcun altro abbia progettato la seguente classe
✔ Senza sapere come sia realizzata StackX,possiamo usarne un esemplare mediante il suocomportamento astratto definito in Stack
✔ Allo stesso modo, possiamo usare un esemplare diun’altra classe StackY che realizza Stack
public class StackX implements Stack{ ...}
12
public class ReverseStack // UN ESEMPIO{ public static void main(String[] args) { Stack s = new StackX(); s.push("Pippo"); s.push("Pluto"); s.push("Paperino"); printAndClear(s); System.out.println(); s.push("Pippo"); s.push("Pluto"); s.push("Paperino"); printAndClear(reverseAndClear(s)); } private static Stack reverseAndClear(Stack s) { Stack p = new StackY(); while (!s.isEmpty()) p.push(s.topAndPop()); return p; } private static void printAndClear(Stack s) { while (!s.isEmpty()) System.out.println(s.topAndPop()); }}
PaperinoPlutoPippo
PippoPlutoPaperino
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 3
13
Realizzazione della pila✔ Per realizzare una pila è facile ed efficiente usare
una struttura di tipo array “riempito solo in parte”✔ Il solo problema che si pone è cosa fare quando
l’array è pieno e viene invocato il metodo push– la prima soluzione proposta prevede il lancio di
un’eccezione– la seconda soluzione proposta usa il
ridimensionamento dell’array✔ Un altro errore da segnalare con un’eccezione è
l’invocazione di pop o top quando la pila è vuota
14
Eccezioni nella pila✔ Definiamo due eccezioni (che sono classi),
EmptyStackException e FullStackException,come estensione di RuntimeException, in modoche chi usa la pila non sia obbligato a gestirlepublic class EmptyStackException extends RuntimeException{}public class FullStackException extends RuntimeException{}
15
Definizione di un’eccezione
✔ La classe è vuota, a cosa serve?– serve a definire un nuovo tipo di dato (un’eccezione)
che ha le stesse identiche caratteristiche dellasuperclasse da cui viene derivato, ma di cui interessaporre in evidenza il nome, che contiene l’informazionedi identificazione dell’errore
✔ Con le eccezioni si fa spesso così– in realtà la classe non è vuota, perché contiene tutto ciò
che eredita dalla sua superclasse
public class EmptyStackException extends RuntimeException{ }
16
public class FixedArrayStack implements Stack{ // v è un array riempito solo in parte protected Object[] v; // considerare protected protected int vSize; // uguale a private public FixedArrayStack() { v = new Object[100]; // 100 è un numero scelto // a caso, va bene anche 1 // per rendere vuota la struttura, invoco // il metodo makeEmpty, è sempre meglio evitare // di scrivere codice ripetuto makeEmpty(); } // dato che Stack estende Container, // occorre realizzare anche i suoi metodi public void makeEmpty() { vSize = 0; } public boolean isEmpty() { return (vSize == 0); } ...}
17
public class FixedArrayStack implements Stack{ ... public void push(Object obj) { if (vSize == v.length) throw new FullStackException(); v[vSize++] = obj; } public Object top() { if (isEmpty()) throw new EmptyStackException(); return v[vSize - 1]; } public void pop() { if (isEmpty()) throw new EmptyStackException(); vSize--; } public Object topAndPop() { Object obj = top(); pop(); return obj; } // dalla definizione di topAndPop si nota che il // metodo non è necessario (ma è utile)}
18
Intervallo
�����������������������
������
��������������
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 4
19
✔ Definiamo una pila che non generi mai l’eccezioneFullStackException
✔ Possiamo evitare di riscrivere tutto il codice diFixedArrayStack in GrowingArrayStack?
public class GrowingArrayStack implements Stack{ public void push(Object obj) { if (vSize == v.length) v = resize(v, 2*vSize); v[vSize++] = obj; } ... // tutto il resto è identico!}
Pila con ridimensionamento
20
✔ Il metodo push sovrascritto deve poter accederealle variabili di esemplare della superclasse
✔ Questo è consentito dalla definizione protected– le variabili protected sono come private, ma vi
si può accedere anche dalle classi derivate (edalle classi che si trovano nello stesso package)
public class GrowingArrayStack extends FixedArrayStack{ public void push(Object obj) { if (vSize == v.length) v = resize(v, 2*vSize); super.push(obj); }}
Pila con ridimensionamento
21
✔ Il progettista della superclasse decide se rendereaccessibile in modo protected lo stato della classe(o una sua parte…)
✔ È una parziale violazione dell’incapsulamento, maavviene in modo consapevole ed esplicito
✔ Anche i metodi possono essere definiti protected– possono essere invocati soltanto all’interno
della classe in cui sono definiti (come i metodiprivate) e all’interno delle classi derivate daessa (nonché all’interno di classi che si trovanonello stesso package)
Accesso protected
22
Prestazioni della pila✔ Il tempo di esecuzione di ogni operazione su una
pila realizzata con array di dimensioni fisse ècostante, cioè non dipende dalla dimensione ndella struttura dati stessa (non ci sono cicli…)– si noti che le prestazioni dipendono dalla
definizione della struttura dati e non dalla suainterfaccia…
– per valutare le prestazioni è necessarioconoscere il codice che realizza le operazioni!
23
Prestazioni della pila✔ Un’operazione eseguita in un tempo costante, cioè
in un tempo che non dipende dalle dimensioni delproblema, ha un andamento asintotico O(1),perché– eventuali costanti moltiplicative vengono
trascurate✔ Ogni operazione eseguita su FixedArrayStack è
quindi O(1)
24
Prestazioni della pila✔ Nella realizzazione con array ridimensionabile,
l’unica cosa che cambia è l’operazione push– “alcune volte” richiede un tempo O(n)
• tale tempo è necessario per copiare tutti gli elementinel nuovo array, all’interno del metodo resize
• il ridimensionamento viene fatto ogni n operazioni
– cerchiamo di valutare il costo medio diciascuna operazione• tale metodo di stima si chiama analisi
ammortizzata delle prestazioni asintotiche
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 5
25
Analisi ammortizzata✔ Dobbiamo calcolare il valore medio di n
operazioni, delle quali– n-1 richiedono un tempo O(1)– una richiede un tempo O(n) T(n)= [(n-1)*O(1) + O(n)]/n
= O(n)/n = O(1)
✔ Distribuendo il tempo speso per ilridimensiomento in parti uguali a tutte leoperazioni push, si ottiene quindi ancora O(1)
26
Analisi ammortizzata✔ Le prestazioni di push con ridimensionamento
rimangono O(1) per qualsiasi costantemoltiplicativa usata per calcolare la nuovadimensione, anche diversa da 2
✔ Se, invece, si usa una costante additiva, cioè ladimensione passa da n a n+k, si osserva che su noperazioni di inserimento quelle “lente” sono n/kT(n)= [(n-n/k)*O(1)+(n/k)*O(n)]/n = [O(n) + n*O(n)]/n = O(n)/n + O(n) = O(1) + O(n) = O(n)
27
Pile di oggetti e pile di numeri✔ Abbiamo visto che la pila così definita è in grado
di gestire dati di qualsiasi tipo, cioè riferimenti adoggetti di qualsiasi tipo (stringhe, conti bancari…)
✔ Non è però in grado di gestire dati dei tipifondamentali definiti nel linguaggio Java (int,double, char…)– tali tipi di dati NON sono oggetti e NON possono
essere assegnati a variabili riferimento di tipo Object✔ Come possiamo gestire una pila di numeri?
28
Pile di numeri✔ Possiamo ridefinire tutto
public interface IntStack extends Container{ void push(int obj); void pop(); int top(); int topAndPop();}
public class FixedArrayIntStack implements IntStack{ protected int[] v; protected int vSize; public FixedArrayIntStack() { v = new int[100]; makeEmpty(); } public void makeEmpty() { vSize = 0; } public boolean isEmpty(){ return (vSize == 0); } public void push(int obj) { if(vSize == v.length)throw new FullStackException(); v[vSize++] = obj; } public int top() { if (isEmpty()) throw new EmptyStackException(); return v[vSize - 1]; } public void pop() { if (isEmpty()) throw new EmptyStackException(); vSize--; } public int topAndPop() { int obj = top(); pop(); return obj; }}
29
Pile di numeri✔ La ridefinizione della pila per ogni tipo di dato
fondamentale ha alcuni svantaggi– occorre replicare il codice nove volte (i tipi di dati
fondamentali sono otto), con poche modifiche– non esiste più il tipo di dati astratto Stack, ma esisterà
IntStack, DoubleStack, CharStack, ObjectStack✔ Cerchiamo un’alternativa, ponendoci un problema
– è possibile “trasformare” un numero intero (o un altrotipo di dato fondamentale di Java) in un oggetto?
✔ La risposta è affermativa– si usano le classi involucro (wrapper)
30
Classi involucro✔ Per dati int esiste la classe involucro Integer
– il costruttore richiede un parametro di tipo int e crea unoggetto di tipo Integer che contiene il valore intero,“avvolgendolo” con la struttura di un oggetto
– gli oggetti di tipo Integer sono immutabili– per conoscere il valore memorizzato all’interno di un
oggetto di tipo Integer si usa il metodo non staticointValue, che restituisce un valore di tipo int
Integer intObj = new Integer(2);Object obj = intObj; // lecito
int x = intObj.intValue();// x vale 2
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 6
31
Classi involucro✔ Esistono classi involucro per tutti i tipi di dati
fondamentali di Java, con i nomi uguali al nomedel tipo fondamentale ma iniziale maiuscola– eccezioni di nomi: Integer e Character
✔ Ogni classe fornisce un metodo per ottenere ilvalore contenuto al suo interno, con il nomecorrispondente al tipo– es: booleanValue( ), charValue( ), doubleValue( )
✔ Tutte le classi involucro si trovano nel pacchettojava.lang e (tranne Boolean) realizzanoComparable 32
��������������������������
��������������������������������
33
Esercizi
34
✔ Vogliamo risolvere il problema di verificare se inun’espressione algebrica (ricevuta come String) leparentesi tonde, quadre e graffe sono utilizzate inmaniera corretta
✔ In particolare, vogliamo verificare che ad ogniparentesi aperta corrisponda una parentesi chiusadello stesso tipo
✔ Risolviamo prima il problema nel caso semplice incui non siano ammesse parentesi annidate
Esercizio: Controllo parentesi
35
✔ Inizializza la variabile booleana x a false (vale truequando ci si trova all’interno di una coppia di parentesi)
✔ Finché la stringa non è finita– leggi nella stringa il carattere più a sinistra non ancora letto– se è una parentesi
• se è una parentesi aperta– se x è false poni x = true e memorizza il tipo di
parentesi– altrimenti errore (parentesi annidate...)
• altrimenti (è una parentesi chiusa…)– se x è false errore (parentesi chiusa senza
aperta...)– altrimenti se corrisponde a quella memorizzata
poni x = false (la parentesi è stata chiusa…)– altrimenti errore (parentesi non corrispondenti)
✔ Se x è true, errore (parentesi aperta senza chiusa)
Esercizio: Algoritmo
36
public static int checkWithoutNesting(String s){ boolean inBracket = false; char bracket = '0'; // un valore qualsiasi for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (isBracket(c)) if (isOpeningBracket(c)) if (!inBracket) { inBracket = true; bracket = c; } else return 1; else if (!inBracket) return 2; else if(areMatching(bracket, c)) inBracket = false; else return 3; } if (inBracket) return 4; return 0; // OK}
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 7
37
private static boolean isOpeningBracket(char c){ return c == '(' || c == '[' || c == '{';}private static boolean isClosingBracket(char c){ return c == ')' || c == ']' || c == '}';}private static boolean isBracket(char c){ return isOpeningBracket(c) || isClosingBracket(c);}private static boolean areMatching(char c1, char c2){ return c1 == '(' && c2 == ')' || c1 == '[' && c2 == ']' || c1 == '{' && c2 == '}';}
38
✔ Cerchiamo di risolvere il caso più generale, in cuile parentesi di vario tipo possono essere annidate
✔ In questo caso non è più sufficiente memorizzareil tipo dell’ultima parentesi che è stata aperta,perché ci possono essere più parentesi aperte chesono in attesa di essere chiuse– quando si chiude una parentesi, bisogna controllare se
corrisponde al tipo della parentesi in attesa che è stataaperta più recentemente
Esercizio: Controllo parentesi
a + [ c + (g + h) + (f + z) ]
39
✔ Possiamo quindi risolvere il problema usando unapila
✔ Effettuando una scansione della stringa da sinistra a destra– inseriamo sulla pila le parentesi aperte– quando troviamo una parentesi chiusa, estraiamo una
parentesi dalla pila (che sarà quindi l’ultima ad esservistata inserita) e controlliamo che i tipi corrispondano,segnalando un errore in caso contrario
• se ci troviamo a dover estrarre da una pila vuota,segnaliamo l’errore (parentesi chiusa senza aperta)
– se al termine della stringa la pila non è vuota,segnaliamo l’errore (parentesi aperta senza chiusa)
Esercizio: Controllo parentesi
40
public static int checkWithNesting(String s){ Stack st = new GrowingArrayStack(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (isBracket(c)) if (isOpeningBracket(c)) st.push(new Character(c)); else try { Object obj = st.topAndPop(); Character ch = (Character)c; char cc = ch.charValue(); if (!areMatching(cc, c)) return 3; } catch (EmptyStackException e) { return 2; } } if (!st.isEmpty()) return 4; return 0;}
41
✔ Abbiamo visto che le strutture dati generiche,definite in termini di Object, sono molto comodeperché possono contenere oggetti di qualsiasi tipo
✔ Sono però un po’ scomode nel momento in cuieffettuiamo l’estrazione (o l’ispezione) di oggettiin esse contenuti– viene sempre restituito un riferimento di tipo Object,
indipendentemente dal tipo di oggetto effettivamenterestituito
– si usa un cast per ottenere un riferimento del tipooriginario Object obj = st.topAndPop();
Character ch = (Character)c;
Estrarre oggetti da una struttura dati
42
✔ Sappiamo che serve il cast perché l’operazione diassegnamento è potenzialmente pericolosa
✔ Il programmatore si assume la responsabilità diinserire nella struttura dati oggetti del tipo corretto
✔ Cosa succede se è stato inserito un oggetto cheNON sia di tipo Character?– viene lanciata l’eccezione ClassCastException
✔ Possiamo scrivere codice che si comporti in modopiù sicuro?
Character ch = (Character)st.topAndPop();
Estrarre oggetti da una struttura dati
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 8
43
✔ Ricordiamo che le eccezioni la cui gestione non èobbligatoria, come ClassCastException, possonocomunque essere gestite!
✔ In alternativa si può usare l’operatore instanceof
Estrarre oggetti da una struttura dati
try{ Character ch = (Character)st.topAndPop();} catch (ClassCastException e){ // gestione dell'errore}
Object obj = st.topAndPop();if (obj instanceof Character) Character ch = (Character)obj;else // gestione dell'errore 44
L’operatore instanceof✔ Sintassi:✔ Scopo: è un operatore booleano che restituisce
true se e solo se la variabileOggetto contiene unriferimento ad un oggetto che è un esemplare dellaclasse NomeClasse (o di una sua sottoclasse)– in tal caso l’assegnamento di variabileOggetto
ad una variabile di tipo NomeClasse NONlancia l’eccezione ClassCastException
✔ Nota: il risultato non dipende dal tipo dichiaratoper la variabileOggetto, ma dal tipo dell’oggetto acui la variabile si riferisce effettivamente almomento dell’esecuzione
variabileOggetto instanceof NomeClasse
45
✔ Vogliamo risolvere il problema di calcolare ilrisultato di un’espressione aritmetica (ricevutacome String) contenente somme, sottrazioni,moltiplicazioni e divisioni
✔ Se l’espressione usa la classica notazione (dettainfissa) in cui i due operandi di un’operazione sitrovano ai due lati dell’operatore, l’ordine diesecuzione delle operazioni è determinato dalleregole di precedenza tra gli operatori e daeventuali parentesi
✔ Scrivere un programma per tale compito èpiuttosto complesso, mentre è molto più facilecalcolare espressioni che usano una diversanotazione
Esercizio: Calcolatrice
46
✔ Un’espressione aritmetica può anche usare lanotazione postfissa, detta anche notazione polaccainversa (RPN, Reverse Polish Notation)
✔ In tale notazione non sono ammesse parentesi (nésarebbero necessarie)
✔ I due operandi di ciascun operatore si trovano allasua sinistra
Notazione postfissa
7 1 2 + 4 * 5 6 + - /
7/[(1+2)*4 - (5+6)]
47
✔ Esiste un semplice algoritmo che usa una pila pervalutare un’espressione in notazione postfissa
✔ Finché l’espressione non è terminata– leggi da sinistra il primo simbolo o valore non letto– se è un valore, inseriscilo sulla pila– altrimenti (è un operatore…)
• estrai dalla pila l’operando sinistro• estrai dalla pila l’operando destro• esegui l’operazione• inserisci il risultato sulla pila
✔ Se la pila contiene più di un valore, l’espressionecontiene un errore
✔ L’unico valore presente sulla pila è il risultato
Notazione postfissa
48
public static double evaluateRPN(String s){ Stack st = new GrowingArrayStack(); StringTokenizer tk = new StringTokenizer(s); while (tk.hasMoreTokens()) { String x = tk.nextToken(); try { Double.parseDouble(x); // è un valore numerico st.push(x); } catch (NumberFormatException e) { // è un operatore double r = evalOperator(x, st.topAndPop(), st.topAndPop()); st.push(Double.toString(r)); } } double r = Double.parseDouble(st.topAndPop()); if (!st.isEmpty()) throw new RuntimeException(); return r;}
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 9
49
private static double evalOperator(String op, String left, String right){ double opLeft = Double.parseDouble(left); double opRight = Double.parseDouble(right); double result; if (op.equals("+")) result = opLeft + opRight; else if (op.equals("-")) result = opLeft - opRight; else if (op.equals("*")) result = opLeft * opRight; else if (op.equals("/")) result = opLeft / opRight; else throw new RuntimeException(); return result;}
50
Intervallo
�������������������������
��������������
51
Rappresentazione deinumeri
52
Rappresentazione dei numeri✔ Riprendiamo il discorso sulla rappresentazione dei
numeri interi all’interno di un calcolatore✔ Abbiamo visto che per i numeri interi positivi si
usa la rappresentazione binaria posizionale (101100)2 = (44)10
✔ Se si usa una rappresentazione a n bit, si possonorappresentare i 2n numeri interi che sono compresinell’intervallo [0 , 2n - 1] ∩ Ν
53
Numeri interi relativi✔ Come possiamo rappresentare i numeri negativi?
– la rappresentazione più naturale è quella dettarappresentazione con modulo e segno
– si rappresenta il segno positivo o negativo delnumero con il primo bit della sequenza (quellopiù a sinistra), quindi si rappresenta il modulo ovalore assoluto del numero, che ovviamente èun numero non negativo
(101100)2 = (-12)10
(001100)2 = (+12)1054
Numeri interi relativi✔ Se si usa una rappresentazione a n bit, si possono
rappresentare i 2n-1 numeri interi che sonocompresi nell’intervallo [-(2n-1 - 1) , 2n-1 - 1] ∩ Ν
✔ Problema: c’è una doppia rappresentazione per lozero (+0 e -0), per cui si “spreca” unaconfigurazione
✔ Problema: l’algoritmo per l’addizione di numericosì rappresentati è complesso
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 10
55
Numeri interi relativi✔ Addizione S = A + B eseguita con numeri
rappresentati in modulo e segno✔ Se segno(A) = segno(B)
segno(S) = segno(A), |S| = ( |A| + |B| ) altrimenti
se |A| ≥ |B| segno(S) = segno(A), |S| =( |A| - |B| )
altrimenti segno(S) = segno(B), |S| =( |B| - |A| )
56
Complemento a due✔ Una rappresentazione più efficiente è quella
denominata complemento a due, così definita– dato un numero intero relativo
a ∈ [-2n-1 , 2n-1 - 1] ∩ Ν la sua rappresentazione in complemento a due è
= C2(a)
rappresentazione binaria a n bit di a se a ≥ 0
rappresentazione binaria a n bit di (a+2n) se a < 0
57
Complemento a due✔ Proprietà (dimostrabili)
– il segno di un numero rappresentato incomplemento a due è ancora il bit più a sinistradella rappresentazione (0 se positivo o nullo, 1se negativo)
– la parte restante della rappresentazione NON èil valore assoluto del numero• lo è soltanto per i numeri positivi
– non ci sono più configurazioni “sprecate”• con n bit si rappresentano 2n numeri diversi
58
Complemento a due✔ Proprietà (dimostrabili)
– per eseguire l’addizione di numeri rappresentatiin complemento a due si esegue semplicementel’addizione binaria delle rappresentazioni,senza pensare al fatto che il bit più a sinistrarappresenta il segno
– al termine dell’addizione, NON bisognaconsiderare un eventuale riporto che si venissea trovare nella posizione n, cioè un’eventuale(n+1)-esima cifra del risultato non ne fa parte
59
Overflow in complemento a due✔ Come per tutte le rappresentazioni numeriche,
anche il complemento a due può dar luogo afenomeni di trabocco (overflow) quando ilrisultato di un’operazione non rientranell’intervallo dei numeri rappresentabili con ilnumero di bit usati dalla rappresentazione
✔ Ad esempio, nell’addizione a+b si ha overflow se
a+b ∉ [-2n-1 , 2n-1 - 1] ∩ Ν✔ Eseguendo l’addizione, come ci si accorge di una
situazione di overflow?60
Overflow in complemento a due✔ Nell’addizione di due numeri in complemento a
due si ha una situazione di overflow se e solo se– si ha un riporto tra la colonna (n-1)-esima e la colonna
n-esima e non si ha un riporto tra la colonna n-esima ela colonna (n+1)-esima
oppure– non si ha un riporto tra la colonna (n-1)-esima e la
colonna n-esima e si ha un riporto tra la colonna n-esima e la colonna (n+1)-esima
✔ Anche questa proprietà è dimostrabile
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 11
61
Numeri interi in Java✔ In Java tutti i tipi di dati fondamentali per numeri
interi usano internamente la rappresentazione incomplemento a due– byte, short, int, long
✔ La JVM non segnala le condizioni di overflownelle operazioni aritmetiche– si ottiene semplicemente un risultato errato
✔ L’unica operazione aritmetica tra numeri interi chegenera un’eccezione è la divisione con divisorezero– ArithmeticException
62
Numeri in virgola mobile in Java✔ In Java tutti i tipi di dati fondamentali per numeri
in virgola mobile usano internamente unarappresentazione binaria codificata dallo standardinternazionale IEEE 754– float, double
✔ La divisione con divisore zero non è un errore seeffettuata tra numeri in virgola mobile– se il dividendo è diverso da zero, il risultato è
infinito (con il segno del dividendo)– se anche il dividendo è zero, il risultato non è
un numero e viene usata la codifica specialeNaN (Not a Number)
63
Numeri in virgola mobile in Java✔ Lo standard IEEE 754 prevede quindi anche la
rappresentazione di NaN, di +∞ e di -∞✔ Sono definite le seguenti costanti
– Double.NaN– Double.NEGATIVE_INFINITY– Double.POSITIVE_INFINITY
✔ e le corrispondenti costanti Float– Float.NaN– Float.NEGATIVE_INFINITY– Float.POSITIVE_INFINITY
64
��������������������������
�������������������������������
65
Coda (queue)
66
Coda (queue)✔ In una coda (queue) gli oggetti possono essere
inseriti ed estratti secondo un comportamentodefinito FIFO (First In, First Out)– il primo oggetto inserito è il primo ad essere estratto– il nome è stato scelto in analogia con persone in coda
✔ L’unico oggetto che può essere ispezionato èquello che verrebbe estratto
✔ Esistono molti possibili utilizzi di una strutturadati con questo comportamento– la simulazione del funzionamento di uno sportello
bancario con più clienti che arrivano in momenti diversiuserà una coda per rispettare la priorità di servizio
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 12
67
Coda (queue)✔ I metodi che caratterizzano una coda sono
– enqueue per inserire un oggetto nella coda– dequeue per esaminare ed eliminare dalla coda
l’oggetto che vi si trova da più tempo– getFront per esaminare l’oggetto che verrebbe
eliminato da dequeue, senza estrarlo
✔ Infine, ogni ADT di tipo “contenitore” ha i metodi– isEmpty per sapere se il contenitore è vuoto– makeEmpty per vuotare il contenitore
68
✔Si notino le similitudini con la pila– enqueue corrisponde a push– dequeue corrisponde a topAndPop– getFront corrisponde a top– pop non ha un metodo analogo nella coda
Coda (queue)public interface Queue extends Container{ void enqueue(Object obj); Object dequeue(); Object getFront();}
69
Coda (queue)✔ Per realizzare una coda si può usare una struttura
di tipo array “riempito solo in parte”, in modosimile a quanto fatto per realizzare una pila
✔ Mentre nella pila si inseriva e si estraeva allostesso estremo dell’array (l’estremo “destro”), quidobbiamo inserire ed estrarre ai due diversiestremi– decidiamo di inserire a destra ed estrarre a sinistra
70
Coda (queue)✔ Come per la pila, anche per la coda bisognerà
segnalare l’errore di accesso ad una coda vuota egestire la situazione di coda piena (segnalando unerrore o ridimensionando l’array)
✔ Definiamo– EmptyQueueException e FullQueueException
public class EmptyQueueException extends RuntimeException{ }
public class FullQueueException extends RuntimeException{ }
71
public class SlowFixedArrayQueue implements Queue{ private Object[] v; private int vSize; public SlowFixedArrayQueue() { v = new Object[100]; makeEmpty(); } public void makeEmpty() { vSize = 0; } public boolean isEmpty() { return (vSize == 0); } public void enqueue(Object obj) { if (vSize == v.length) throw new FullQueueException(); v[vSize++] = obj; } public Object getFront() { if (isEmpty()) throw new EmptyQueueException(); return v[0]; } public Object dequeue() { Object obj = getFront(); vSize--; for (int i = 0; i < vSize; i++) v[i] = v[i+1]; return obj; }}
72
Coda (queue)✔ Questa semplice realizzazione con array, che
abbiamo visto essere molto efficiente per la pila, èal contrario assai inefficiente per la coda– il metodo dequeue è O(n), perché bisogna
spostare tutti gli oggetti della coda per fare inmodo che l’array rimanga “compatto”
– la differenza rispetto alla pila è dovuta al fattoche nella coda gli inserimenti e le rimozioniavvengono alle due estremità diverse dell’array,mentre nella pila avvengono alla stessaestremità
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 13
73
Coda (queue)✔ Per realizzare una coda più efficiente servono due
indici anziché uno soltanto– un indice punta al primo oggetto della coda e
l’altro indice punta all’ultimo oggetto dellacoda
✔ In questo modo, aggiornando opportunamente gliindici, si ottiene la realizzazione di una coda conun “array riempito solo nella parte centrale” incui tutte le operazioni sono O(1)– la gestione dell’array pieno ha le due solite
soluzioni, ridimensionamento o eccezione
74
public class FixedArrayQueue implements Queue{ protected Object[] v; protected int front, back; public FixedArrayQueue() { v = new Object[100]; makeEmpty(); } public void makeEmpty() { front = back = 0; } public boolean isEmpty() { return (back == front); } public void enqueue(Object obj) { if (back == v.length) throw new FullQueueException(); v[back++] = obj; } public Object getFront() { if (isEmpty()) throw new EmptyQueueException(); return v[front]; } public Object dequeue() { Object obj = getFront(); front++; return obj; }}
75
public class GrowingArrayQueue extends FixedArrayQueue{ public void enqueue(Object obj) { if (back == v.length) v = resize(v, 2*v.length); super.enqueue(obj); }}
Coda (queue)✔ Per rendere la coda ridimensionabile, usiamo la
stessa strategia vista per la pila, estendendo laclasse FixedArrayQueue e sovrascrivendo il solometodo enqueue
76
Prestazioni della coda✔ La realizzazione di una coda con un array e due
indici ha la massima efficienza in termini diprestazioni temporali, tutte le operazioni sonoO(1), ma ha ancora un punto debole
✔ Se l’array ha N elementi, proviamo a– effettuare N operazioni enqueue
e poi– effettuare N operazioni dequeue
✔ Ora la coda è vuota, ma alla successivaoperazione enqueue l’array sarà pieno– lo spazio di memoria non viene riutilizzato
77
Coda con array circolare✔ Per risolvere quest’ultimo problema si usa una
tecnica detta “array circolare”– i due indici, dopo essere giunti alla fine
dell’array, possono ritornare all’inizio se sisono liberate delle posizioni
– in questo modo l’array risulta pieno solo se lacoda ha effettivamente un numero di oggettiuguale alla dimensione dell’array
– le prestazioni temporali rimangono identiche
78
public class FixedCircularArrayQueue extends FixedArrayQueue{ // il metodo increment fa avanzare un indice di una // posizione, tornando all’inizio dell’array se si supera // la fine protected int increment(int index) { return (index + 1) % v.length; } public void enqueue(Object obj) { if (increment(back) == front) throw new FullQueueException(); v[back] = obj; back = increment(back); } public Object dequeue() { Object obj = getFront(); front = increment(front); return obj; }}
Coda con array circolare
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 14
79
public class GrowingCircularArrayQueue extends FixedCircularArrayQueue{ public void enqueue(Object obj) { if (increment(back) == front) { v = resize(v, 2*v.length); // se si ridimensiona l’array e la zona utile // della coda si trova attorno alla sua fine, // la seconda metà del nuovo array rimane vuota // e provoca un malfunzionamento della coda, // che si risolve spostandovi la parte della // coda che si trova all’inizio dell’array if (back < front) { System.arraycopy(v, 0, v, v.length/2, back); back += v.length/2; } } super.enqueue(obj); }}
Coda con array circolare
80
Intervallo
�������������������������
��������������
81
Passaggio di parametri pervalore e per riferimento
82
✔ Abbiamo visto il metodo increment che ha ilcompito di fornire un nuovo valore per unavariabile di tipo numerico
✔ Perché non abbiamo scritto più semplicemente
// si usa con x = increment(x)protected int increment(int index){ return (index + 1) % v.length;}
Modificare parametri numerici
// si usa con increment(x)protected void increment(int index){ index = (index + 1) % v.length;}
83
Modificare parametri numerici// si usa con increment(x)protected void increment(int index){ index = (index + 1) % v.length;} // NON FUNZIONA
x
index
invocando il metodo il valore viene copiato
in increment si modificaquesto valorequando si torna nel metodo invocante
il valore di x non è stato modificato
84
// NON FUNZIONApublic static void swapAccounts(BankAccount x, BankAccount y){ BankAccount temp = x; x = y; y = temp;}
Modificare parametri numerici✔ Abbiamo visto che un metodo può invece
modificare lo stato di un oggetto passato comeparametro (implicito o esplicito)– ma non può modificare il riferimento contenuto nella
variabile oggetto che ne costituisce il parametro effettivo
swapAccounts(a, b);// nulla è successo alle// variabili a e b
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 15
85
Chiamate per valore e per riferimento
✔ In Java, il passaggio dei parametri è effettuato “pervalore”, cioè il valore del parametro effettivo vieneassegnato al parametro formale– questo impedisce che il valore contenuto nella variabile
che costituisce il parametro effettivo possa esseremodificato
✔ Altri linguaggi di programmazione (come C++)consentono di effettuare il passaggio dei parametri“per riferimento”, rendendo possibile la modificadei parametri effettivi
86
Chiamate per valore e per riferimento
✔ A volte si dice, impropriamente, che in Java inumeri sono passati per valore e che gli oggettisono passati per riferimento
✔ In realtà, tutte le variabili sono passate per valore,ma– passando per valore una variabile oggetto, si passa una
copia del riferimento in essa contenuto– l’effetto “pratico” del passaggio per valore di un
riferimento ad un oggetto è la possibilità di modificarelo stato dell’oggetto stesso, come avviene con ilpassaggio “per riferimento”
87
Ordinare pile e code
88
✔ Proviamo a realizzare l’ordinamento per fusione(Mergesort) applicato ad una pila anziché ad unarray– ovviamente la pila deve contenere oggetti Comparable
✔ Il primo problema consiste nella suddivisionedella pila in due pile di dimensioni circa uguali– non conosciamo la dimensione della pila– una soluzione semplice consiste nell’estrarre gli
elementi dalla pila ed inserirli alternativamentenelle due semi-pile
Esercizio: Mergesort per pila
89
public static void mergeSort(Stack s){ if (s == null || s.isEmpty()) return; // caso base Object temp = s.topAndPop(); if (s.isEmpty()) { s.push(temp); // altro caso base: dimensione 1 return; } // dividiamo (circa) a meta’ Stack left = new GrowingArrayStack(); Stack right = new GrowingArrayStack(); left.push(temp); boolean flag = true; while (!s.isEmpty()) if (flag = !flag) // attenzione al trucco... left.push(s.topAndPop()); else right.push(s.topAndPop()); // passi ricorsivi per problemi piu’ semplici mergeSort(left); mergeSort(right); // fusione: si osservi che ora s è vuota merge(s, left, right);}
90
private static void merge(Stack s, // NON FUNZIONA Stack left, Stack right){ while (!left.isEmpty() && !right.isEmpty()) { Comparable x = (Comparable) left.top(); Comparable y = (Comparable) right.top(); if (x.compareTo(y) < 0) s.push(left.topAndPop()); else s.push(right.topAndPop()); } while (!left.isEmpty()) s.push(left.topAndPop()); while (!right.isEmpty()) s.push(right.topAndPop()); // a questo punto s contiene i dati ordinati, // ma in cima alla pila c’è l’elemento maggiore!}
Fusione di due pile ordinate
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 16
91
private static void merge(Stack s, Stack left, Stack right){ Stack temp = new GrowingArrayStack(); while (!left.isEmpty() && !right.isEmpty()) { Comparable x = (Comparable) left.top(); Comparable y = (Comparable) right.top(); if (x.compareTo(y) < 0) temp.push(left.topAndPop()); else temp.push(right.topAndPop()); } while (!left.isEmpty()) temp.push(left.topAndPop()); while (!right.isEmpty()) temp.push(right.topAndPop()); while (!temp.isEmpty()) s.push(temp.topAndPop());}
Fusione di due pile ordinate
92
✔ Notiamo che, nonostante l’apparente maggiorecomplessità, le prestazioni asintotiche rimangonoO(n lg n)
✔ L’analisi dell’algoritmo (ricordando che tutte leoperazioni sulle pile sono O(n) ) fornisce ancora
T(n) = 2T(n/2) + O(n) da cui si ottiene di nuovo
O(n lg n)
Esercizio: Mergesort per pila
93
✔ Proviamo a realizzare l’ordinamento per fusione(Mergesort) applicato ad una coda anziché ad unarray– ovviamente la coda deve contenere oggetti
Comparable✔ La soluzione del problema è molto simile alla
soluzione vista per la pila
Esercizio: Mergesort per coda
94
public static void mergeSort(Queue q){ if (q == null || q.isEmpty()) return; // caso base Object temp = q.dequeue(); if (q.isEmpty()) { q.enqueue(temp); // altro caso base: dimensione 1 return; } // dividiamo (circa) a meta’ Queue left = new GrowingCircularArrayQueue(); Queue right = new GrowingCircularArrayQueue(); left.enqueue(temp); boolean flag = true; while (!q.isEmpty()) if (flag = !flag) left.enqueue(q.dequeue()); else right.enqueue(q.dequeue()); // passi ricorsivi per problemi piu’ semplici mergeSort(left); mergeSort(right); // fusione: si osservi che ora q è vuota merge(q, left, right);}
95
private static void merge(Queue q, Queue left, Queue right){ while (!left.isEmpty() && !right.isEmpty()) { Comparable x = (Comparable) left.getFront(); Comparable y = (Comparable) right.getFront(); if (x.compareTo(y) < 0) q.enqueue(left.dequeue()); else q.enqueue(right.dequeue()); } while (!left.isEmpty()) q.enqueue(left.dequeue()); while (!right.isEmpty()) q.enqueue(right.dequeue());}
Fusione di due code ordinate
96
✔ Di nuovo, le prestazioni asintotiche sono O(n lg n)
✔ L’analisi dell’algoritmo (ricordando che tutte leoperazioni sulle code sono O(n) ) fornisce ancora
T(n) = 2T(n/2) + O(n) da cui si ottiene di nuovo
O(n lg n)
Esercizio: Mergesort per coda
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 17
97
��������������������������������
������������������������������������
98
Esercizio
99
✔ Vogliamo risolvere il problema di determinaretutte le possibili permutazioni dei caratteri presentiin una stringa– facciamo l’ipotesi che non ci siano caratteri ripetuti
✔ Ricordiamo dalla matematica combinatoria che ilnumero di permutazioni di N simboli è N!
✔ Esempio: ABC– ABC ACB BAC BCA CAB CBA
Esercizio: Permutazioni
100
✔ Esiste un semplice algoritmo ricorsivo per trovarele permutazioni di una stringa di lunghezza N
✔ Se N vale 1, l’unica permutazione è la stringastessa
✔ Altrimenti– estrai il primo carattere dalla stringa– calcola le (N-1)! permutazioni della stringa rimanente– per ognuna delle permutazioni (incomplete) ottenute
• genera N permutazioni (complete) inserendo il carattereprecedentemente estratto in ognuna delle posizioni possibilinella permutazione incompleta
Esercizio: Permutazioni
101
✔ Analizziamo meglio questo punto– calcola le (N-1)! permutazioni della stringa rimanente– per ognuna delle permutazioni (incomplete) ottenute
• genera N permutazioni (complete) inserendo il carattereprecedentemente estratto in ognuna delle posizioni possibilinella permutazione incompleta
✔ Le posizioni in cui si può inserire un carattere inuna delle permutazioni incomplete, che hadimensione N-1, sono le N-2 posizioni che sitrovano tra due caratteri, più la posizione iniziale ela posizione finale– sono quindi N posizioni diverse
Esercizio: Permutazioni
102
public static String[] permutations(String p){ // gestiamo i casi base if (p == null || p.length() == 0) return new String[0]; // oppure return null if (p.length() == 1) return new String[] {p}; // isoliamo il primo carattere String c = p.substring(0,1); // passo ricorsivo String[] cc = permutations(p.substring(1)); // numero di permutazioni da generare String[] r = new String[p.length() * cc.length]; for (int i = 0; i < p.length(); i++) for (int j = 0; j < cc.length; j++) { String s = cc[j]; String sLeft = s.substring(0,i); String sRight = s.substring(i); r[i*cc.length+j] = sLeft + c + sRight; } return r;}
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 18
103
✔ Alcuni commenti generali sulla gestione dellecondizioni “eccezionali”– il metodo riceve un riferimento ad una stringa: tale
riferimento può essere null oppure la stringa può esserevuota (cioè avere lunghezza zero)
• in entrambi i casi il metodo ricorsivo nonfunzionerebbe correttamente, quindi li inseriamocome casi base della ricorsione
– cosa restituiamo?• quando un metodo deve restituire un oggetto, nei
casi in cui riceve un parametro non corretto di solitorestituisce null
Esercizio: Permutazioni
if (p == null || p.length() == 0) return null; 104
✔ In questo caso il metodo deve restituire un oggettodi tipo un po’ particolare, in quanto è un array
✔ Sarebbe comunque corretto restituire null, ma disolito si preferisce restituire un array di lunghezzazero (perfettamente lecito), in modo che il metodoinvocante riceva un array valido, seppure vuoto
✔ In questo modo, il codice seguente funziona…
mentre non funzionerebbe se restituissimo null
Esercizio: Permutazioni
String[] x = permutations("");for (int i = 0; i < x.length; i++) System.out.println(x[i]);
if (p == null || p.length() == 0) return new String[0];
105
✔ Notiamo che anche l’ordine in cui vengonovalutate le due condizioni è molto importante
✔ Se p è null, la prima condizione è vera e per lastrategia di cortocircuito nella valutazionedell’operatore || la seconda condizione nonviene valutata– se venisse valutata, verrebbe lanciata
l’eccezione NullPointerException !!
Esercizio: Permutazioniif (p == null || p.length() == 0) return new String[0];
106
✔ Per calcolare la dimensione del vettore checonterrà le permutazioni, sfruttiamo leinformazioni ottenute dall’invocazione ricorsiva– il numero di permutazioni è uguale alla dimensione
della stringa moltiplicata per il numero di permutazioni(incomplete) già generate
Esercizio: Permutazioni// isoliamo il primo carattereString c = p.substring(0,1);// passo ricorsivoString[] cc = permutations(p.substring(1));// numero di permutazioni da generareString[] r = new String[p.length() * cc.length];
107
✔ Per completare le permutazioni inseriamo ilcarattere c in tutte le posizioni possibili inciascuna permutazione incompleta s
✔ Per ogni s, calcoliamo le sottostringhe cheverranno concatenate a sinistra e a destra di c
✔ Sfruttiamo il fatto che il metodo substringgestisce in modo congruente le situazioni“anomale”
Esercizio: Permutazionifor (int i = 0; i < p.length(); i++) for (int j = 0; j < cc.length; j++) { String s = cc[j]; String sLeft = s.substring(0,i); String sRight = s.substring(i); r[i*cc.length+j] = sLeft + c + sRight; }
108
✔ Quando i vale 0, substring restituisce una stringavuota, che è proprio ciò che vogliamo
✔ Quando i vale p.length( )-1 (suo valore massimo),vale anche s.length( )– in questo caso particolare, substring non lancia
eccezione, ma restituisce una stringa vuota, che èproprio ciò che vogliamo
Esercizio: PermutazioniString sLeft = s.substring(0,i);
String sRight = s.substring(i);
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 19
109
✔ Analizziamo meglio questa espressione dell’indice✔ Globalmente, tale indice deve andare da 0 ap.length() * cc.length (escluso)
✔ Verifichiamo innanzitutto i limiti– per i = 0 e j = 0, l’indice vale 0– per i = p.length()-1 e j = cc.length-1,
l’indice vale (p.length()-1)*cc.length + cc.length - 1 =
p.length()*cc.length - 1 (come volevamo)
Esercizio: Permutazionir[i*cc.length+j] = sLeft + c + sRight;
110
✔ Alla prima iterazione di i, l’indice varia tra 0 ecc.length-1 (perché i vale 0)
✔ Alla seconda iterazione di i, l’indice varia tra1*cc.length+0 = cc.length e1*cc.length+cc.length-1 =2*cc.length-1
✔ Si osserva quindi che gli indici vengono generaticonsecutivamente, senza nessun valore mancante esenza nessun valore ripetuto
Esercizio: Permutazionir[i*cc.length+j] = sLeft + c + sRight;
111
✔ Proviamo ora ad estendere il problema, percalcolare le permutazioni di oggetti genericianziché di caratteri in una stringa
✔ L’algoritmo sarà lo stesso, ma il metodo– riceverà un array di N oggetti anziché una stringa– dovrà restituire un array bidimensionale di oggetti, con
N! righe e N colonne• ciascuna riga conterrà una permutazione
Esercizio: Permutazioni
112
public static Object[][] permutations(Object[] p){ if (p == null || p.length == 0) return new Object[0][0]; if (p.length == 1) { Object[][] r = new Object[1][1]; r[0][0] = p[0]; return r; } Object c = p[0]; Object[] p2 = new Object[p.length-1]; System.arraycopy(p, 1, p2, 0, p2.length); Object[][] cc = permutations(p2); Object[][] r = new Object[p.length*cc.length][p.length]; for (int i = 0; i < p.length; i++) for (int j = 0; j < cc.length; j++) { int index = i * cc.length + j; for (int k = 0; k < i; k++) r[index][k] = cc[j][k]; r[index][i] = c; for (int k = i; k < p.length - 1; k++) r[index][k+1] = cc[j][k]; } return r;}
113
Intervallo
�������������������
���
������������
114
Il metodo equals
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 20
115
✔ Tutto quanto visto sulle permutazioni funzionacorrettamente se è valida l’ipotesi iniziale– gli oggetti da permutare sono tutti distinti
✔ In caso contrario, le permutazioni generate nonsono tutte distinte– ci poniamo il problema di verificare, all’inizio del
programma, che tutti gli oggetti dell’insieme sianodistinti
✔ Più in generale, risolviamo il problema dideterminare se in un array di oggetti esistonooggetti ripetuti, cioè (per essere più precisi)oggetti con identiche proprietà di stato
Esercizio: Oggetti distinti
116
✔ Questo metodo però non funziona correttamente– verifica se nell’array esistono riferimenti che
puntano allo stesso oggetto
public static boolean areUnique(Object[] p){ if (p == null) return true; for (int i = 0; i < p.length; i++) // ogni oggetto viene confrontato // con tutti i successivi for (int j = i+1; j < p.length; j++) if (p[i] == p[j]) return false; return true;}
Esercizio: Oggetti distinti
117
Esercizio: Oggetti distinti✔ Per verificare, invece, che non esistano riferimenti
che puntano ad oggetti (eventualmente diversi)con le medesime proprietà di stato, occorreconfrontare il contenuto (lo stato) degli oggettistessi, e non solo i loro indirizzi in memoria
✔ Lo stato degli oggetti non è generalmenteaccessibile dall’esterno dell’oggetto stesso
✔ Come risolvere il problema?– sappiamo che per le classi della libreria
standard possiamo invocare il metodo equals118
Il metodo equals✔ Come è possibile che il metodo equals venga invocato per
qualsiasi tipo di oggetto?– il metodo equals è definito nella classe Object
✔ Essendo definito in Object, equals viene ereditato da tuttele classi Java, quindi può essere invocato con qualsiasioggetto, come toString– il comportamento ereditato, se non sovrascritto, svolge
la stessa funzione del confronto tra i riferimenti cheabbiamo visto prima
public boolean equals(Object obj){ return (this == obj);}
119
Sovrascrivere il metodo equals✔ Per consentire il confronto per uguaglianza tra due
oggetti di una classe in modo che venga esaminatolo stato degli oggetti stessi, occorre sovrascrivereil metodo equals
public class BankAccount{ public boolean equals(Object obj) { BankAccount other = (BankAccount) obj; return (balance == other.balance); } ...}
120
✔ Questo metodo funziona correttamente se glioggetti appartengono a classi che hannosovrascritto il metodo equals– non ci sono alternative
public static boolean areUnique(Object[] p){ if (p == null) return true; for (int i = 0; i < p.length; i++) // ogni oggetto viene confrontato // con tutti i successivi for (int j = i+1; j < p.length; j++) if (p[i].equals(p[j])) return false; return true;}
Esercizio: Oggetti distinti
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 21
121
Gestione di file in Java
122
Gestione di file in Java✔Finora abbiamo visto programmi Java che
interagiscono con l’utente soltanto tramite iflussi di ingresso e di uscita standard– ciascuno di tali flussi può essere collegato ad un
file con un comando di sistema operativo✔Ci chiediamo: è possibile leggere e scrivere
file in un programma Java?– con la redirezione di input/output, ad esempio,
non possiamo leggere da due file o scrivere sudue file...
123
Gestione di file in Java✔Limitiamoci ad affrontare il problema della
gestione di file di testo (file contenenticaratteri)– esistono anche i file binari, che contengono
semplicemente configurazioni di bit cherappresentano qualsiasi tipo di dati
✔La gestione dei file avviene interagendo conil sistema operativo mediante classi delpacchetto java.io della libreria standard
124
✔Prima di leggere caratteri da un file(esistente) occorre aprire il file in lettura– questa operazione si traduce in Java nella
creazione di un oggetto di tipo FileReader
– il costruttore necessita del nome del file sottoforma di stringa
– se il file non esiste, viene lanciata l’eccezioneFileNotFoundException, che deve esseregestita
Lettura di file di testo
FileReader reader = new FileReader("file.txt");
125
✔ Con l’oggetto di tipo FileReader si può invocareil metodo read che restituisce un carattere ad ogniinvocazione, iniziando dal primo carattere del filee procedendo fino alla fine del file stesso
✔ Non è possibile tornare indietro e rileggerecaratteri già letti– bisogna creare un nuovo oggetto di tipo FileReader
Lettura di file di testo
FileReader reader = new FileReader("file.txt");while(true){ int x = reader.read(); // read restituisce un if (x == -1) break; // intero che vale -1 char c = (char) x; // se il file è finito // elabora c} // il metodo lancia IOException, da gestire
126
✔ Al termine della lettura del file (che nonnecessariamente procede fino alla fine…) occorrechiudere il file
✔ Anche questo metodo lancia IOException, dagestire obbligatoriamente
✔ Se non viene invocato non si ha un errore, ma unapotenziale situazione di instabilità per il sistemaoperativo
Lettura di file di testo
FileReader reader = new FileReader("file.txt");...reader.close();
Fondamenti di Informatica 1 Settimana 6
Marcello Dalpasso 22
127
✔Prima di scrivere caratteri in un file occorreaprire il file in scrittura– questa operazione si traduce in Java nella
creazione di un oggetto di tipo FileWriter
– il costruttore necessita del nome del file sottoforma di stringa e può lanciare l’eccezioneIOException, che deve essere gestita• se il file non esiste, viene creato• se il file esiste, il suo contenuto viene
sovrascritto con i nuovi contenuti
Scrittura di file di testo
FileWriter writer = new FileWriter("file.txt");
128
✔ Con l’oggetto di tipo FileWriter si può invocare ilmetodo write, un metodo sovraccarico checonsente di scrivere– singoli caratteri– array di caratteri– stringhe
✔ Anche questo metodo lancia IOException, dagestire obbligatoriamente
✔ Diversamente da print/println, però, write nonaccetta generici oggetti come parametri, per cuioccorre invocare esplicitamente toString
Scrittura di file di testo
129
✔ Al termine della scrittura del file occorre chiudereil file
✔ Anche questo metodo lancia IOException, dagestire obbligatoriamente
✔ Se non viene invocato non si ha un errore, ma èpossibile che la scrittura del file non vengaultimata prima della terminazione delprogramma, lasciando il file incompleto
Scrittura di file di testo
FileWriter writer = new FileWriter("file.txt");...writer.close();
130
Gestione di file in Java✔Usando le classi FileReader e FileWriter
del pacchetto java.io è quindi possibilemanipolare, all’interno di un programmaJava, più file in lettura e/o più file inscrittura
✔Rimane invariata la possibilità di utilizzare iflussi di ingresso e di uscita standard, chepossono in realtà essere collegati a filesenza che il programma Java ne siaconsapevole
131
������������������������
����������
������������������������������������