Esercizi Java
Esercizi Java
** R I . 13. S criv ete u n a lg o r itm o per risp o n d ere a qu esta d om an d a: un c o n to b a n cario c o n tie n e
in izia lm en te $ 1 0 0 0 0 ; gli interessi v e n g o n o calcolati m e n silm e n te , al tasso a n n u o d el 6% (c io è 0.5%
al m ese); o g n i m e se v e n g o n o prelevati $ 5 0 0 per pagare le sp ese d el c o n v itto u n iversitario; d o p o
q u an ti an n i il c o n t o è v u o to ?
R1.15. Per fare il p rev en tiv o del c o sto di tin teggiatu ra di una casa, !’im b ia n c h in o d ev e c o n o sc e r e
l ’area della sua su p erfìcie esterna. S v ilu p p a te u n a lg o r itm o c h e c a lco li tale valore, a v en d o c o m e
dati in in gresso l ’altezza della casa e le su e d u e d im e n sio n i o r iz zo n ta li (c h ia m ia m o le lu n g h e zz a e
largh ezza), oltre al n u m er o di finestre e p o r te e alle relative d im e n sio n i (n e ll’ip o tesi c h e tu tte le
fin estre a b b ian o le stesse d im e n sio n i, al pari di tu tte le p orte).
** R I . 16. N e l p rob lem a d e sc r itto n ei C o n sig li pratici 1.1, per con fron tare a u to m o b ili can d id ate
all’a cq u isto si s o n o fatte ip o te si sul p rezzo d el carburante e suH’u tiliz z o an n u ale deH’a u to m o b ile .
In lin ea te o rica , sarebbe m e g lio sapere q u ale vettura sia la m ig lio r e senza d over fare tali ip o tesi.
P erch é un p rogram m a e se g u ito al ca lco la to re n o n è in grad o di risolvere tale problem a?
E l . l . S criv ete un p rogram m a c h e v isu a lizzi u n m e ssa g g io di saluto a vostra scelta, m agari in una
lin gu a diversa dall’in g lese.
E 1 .5 . S criv ete un p rogram m a c h e v isu alizzi il vostro n o m e all’in te rn o di un rettan golo. Fate q u an to
p o ssib ile p er c o m p o r r e i lati d el r e tta n g o lo c o n i caratteri 1 - +, c o m e n e ll’e se m p io seg u en te:
+----+
IDave I
in
:«c 3 (c * 3(c H
c
♦♦ ♦♦ ♦ *
E 1 .8 . S criv ete un p rogram m a c h e, u sa n d o caratteri, visu alizzi un v iso sim ile a q u esto (m a diverso):
/////
(I 0 0 I)
I " I
I I
+----- +
Introduzione 31
E l . 9 . S criv ete u n p rogram m a c h e visu alizzi u n ’im ita z io n e di un quadro di P iet M o n d r ia n (se n o n
c o n o sc e te l’artista, fate una ricerca in In ternet). Per rappresentare aree di c o lo r i diversi usate seq u en ze
di caratteri diversi, c o m e @@@ o p p u re : : :, u san d o i caratteri - e | p er c o m p o r re , risp ettiv a m en te,
lin e e o r iz zo n ta li e verticali.
I I
I I I I
+-+-+-+
E 1 .1 1 . S criv ete un p rogram m a c h e visu alizzi u n an im ale m en tre p ro n u n cia un saluto, sim ile a
q u esto (m a diverso):
/\_/\ ...
( ' ' ) / Hello \
( - ) < Iunior I
III \ Coder!/
(_ l_ ) .......
E 1 .1 2 . S criv ete u n program m a c h e , su tre rig h e c o n se c u tiv e , visu alizzi tre strin g h e, ad e se m p io i
n o m i d ei vostri m ig lio r i a m ici o i vostri film p referiti.
E 1 . 1 4 . S criv ete un p rogram m a c h e visu alizzi la bandiera d eg li Stati U n iti d ’A m e ric a u san d o
so lta n to i caratteri * e =.
import javax.swing.lOptionPane;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.DOptionPane;
* E1.20 (economia). N e g li Stati U n it i d ’A m e ric a n o n e sisto n o tasse federali su gli acq u isti, per
cu i c ia scu n o stato p u ò im p o rre le p rop rie. C erca te in In tern et le p ercen tu a li della tassazion e su gli
acquisti applicata da c in q u e stati, p o i scrivere u n p rogram m a c h e le visu alizzi c o n q u esto form ato:
Alaska: 0%
Hawaii: 4%
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
U tilizzare oggetti 79
java.awt.Color java.awt.Rectangle
java.awt.Component getHeight
getHeight getWidth
getWidth getX
setSize getY
setVisible setSize
java.awt.Frame translate
setTitle java.lang.String
java.awt.geom.EllipseZD.Double length
java.awt.geom.LineZD.Double replace
java.awt.geom.PointZD.Double toLowerCase
java.awt.Graphics toUpperCase
setColor javax.swing.!Component
java.awt.GraphicsZD paintComponent
draw javax.swing.DFrame
drawString setDefaultCloseOperation
fili
in t mystery = 1;
80 C apitolo 2
mystery = I - 2 * mystery;
mystery = mystery + l;
int mystery = l;
mystery = mystery + 1;
int mystery = 1 - 2 * mystery;
R 2 .9 . S crivete en u n ciati Java c h e in izia lizzin o la variabile stringa message c o n "Hello", m o d ifica n d o n e
p o i il c o n te n u to in "HELLO". U sa te il m e to d o toUpperCase.
R 2 . 1 1. S criv ete en u n cia ti Java c h e in izia liz z in o la variab ile str in g a message c o n u n m e ssa g g io c o m e
"Hello, World", e lim in a n d o v i p o i i segn i di p u n teggiatu ra m e d ia n te r ip etu te in v o c a zio n i d el m e to d o
replace.
R 2 . 1 3 . S criv ete en u n cia ti Java p er costru ire un o m e t t o della c la sse Rectangle e per dichiarare una
v a r ia b i le o g g e tto della stessa classe.
a. U n rettan golo c o n il cen tro nel p u n to di coo rd in a te (100,100) e c o n la lu n g h ezza di tutti i lati
u gu ale a 50.
b. La stringa "Hello, Dave!".
R 2 .1 6 . S crivete un e n u n cia to Java c h e in izializzi una variab ile square c o n un rettan golo il cu i vertice
su p erio re sinistro abbia c o o rd in a te (10,20) e i cu i lati a b b ia n o tu tti lu n g h e zz a 40. S criv ete, p o i, un
e n u n c ia to c h e sostitu isca il c o n te n u to di square c o n u n r e tta n g o lo av en te le stesse d im e n sio n i ma
v e rtice su p erio re sinistro p o s iz io n a to n el p u n to (2 0 , 2 0 ).
R 2 . 1 7 . S criv ete en u n cia ti Java c h e in iz ia liz z in o d u e variab ili, squarel e square2, in m o d o c h e fac
cia n o r ife r im e n to al m e d e sim o qu adrato, i cu i lati ab b ia n o tu tti lu n g h e z z a 40 e il cu i c en tro sia
p o s iz io n a to n el p u n to di c o o rd in a te (2 0 , 2 0 ).
• c o n c a te n a r e d u e str in g h e , c io è c o stru ire un a strin ga c o stitu ita dalla p rim a strin ga, se g u ita
dalla secon d a;
• elim in are da una stringa gli ev en tu a li spazi in iziali e finali;
• co n v ertire u n r etta n g o lo in un a stringa;
• in d ivid u are il più p ic c o lo retta n g o lo c h e c o n tie n e d u e rettan goli dati;
• restituire u n n u m er o casuale in v irgola m o b ile.
** R 2 .2 3 ( g r a f i c a ) . C h i in v o ca il m e to d o paintComponent di u n c o m p o n e n te grafico? E q u a n d o v ie n e
in vocato?
Esercizi di programmazione
E 2 .1 ( c o l l a u d o ) . S criv ete il p rogram m a AreaTester c h e costru isca u n o g g e tto di tip o Rectangle,
n e ca lco li l’area e la visu alizzi. U sa te i m e to d i getWidth e getHeight e visu alizzate a n c h e il valore
previsto.
E2.3 . S criv ete u n p rogram m a c h e in izia lizzi una stringa al valore "Mississippi", per p o i sostitu irvi
tu tte le lettere "i" c o n "ii", v isu a lizza n d o in fin e la lu n g h e zz a della stringa o tten u ta . In tale stringa,
p o i, si s o s titu is c o n o tu tte le strin g h e "ss" c o n "s", visu a lizza n d o di n u o v o la lu n g h e zz a della stringa
co sì o tten u ta .
E 2 .4 . S criv ete un p rogram m a c h e costru isca un retta n g o lo aven te area 4 2 e u n r etta n g o lo aven te
p e r im e tro 4 2 , v isu a lizza n d o largh ezza e altezza di en tram b i.
S criv ete il p rogram m a AddTester c h e v isu alizzi i valori previsti ed effettiv i della p o s iz io n e , della
largh ezza e d e ll’altezza d el retta n g o lo box d o p o l ’in v o c a z io n e di add.
E2.6 (collaudo). S criv ete il p rogram m a ReplaceTester c h e c o d ific h i una stringa so stitu en d o ,
m e d ia n te il m e to d o replace, tu tte le lettere "i" c o n "!" e tu tte le lettere "s" c o n Fate v ed ere
c h e riu scite a c o d ifica re c o rr etta m e n te la stringa "Mississippi", v isu a lizza n d o il risu ltato p r o d o tto
e q u e llo previsto.
E2.7. S criv ete il p rogram m a HollePrinter c h e scam b i tra lo ro le lettere "e" e "o" in una stringa,
u san d o r ip etu ta m en te il m e to d o replace. Fate v ed ere c h e la stringa "Hello, World! " si trasform a in
"Holle, Werldi".
import java.awt.Color;
import javax.swing.DFrame;
E2.13. S criv ete il p rogram m a RandomPrice c h e visu alizzi u n p rezzo casuale c o m p r e so tra $ 1 0 .0 0 e
$ 1 9 .9 5 o g n i volta c h e v ie n e eseg u ito .
U tilizzare oggetti 83
E2.18 (grafica). S criv ete un p rogram m a g ra fico c h e d iseg n i d u e quadrati aventi il m e d e sim o
cen tro. P rogettate la classe TwoSquareViewer e la classe TwoSquareComponent.
E2.19 (grafica). S criv ete u n p rogram m a gra fico c h e d iseg n i d u e quadrati c o n l’intera su p erficie
colo ra ta, u n o rosa e u n o v iola. Per u n o d ei quadrati usate u n o d ei c o lo r i p red efin iti, m en tre per
l’altro usate u n c o lo r e p erson alizzato. P rogettate la classe TwoSquareViewer e la classe TwoSquare-
Component.
E2.20 (grafica). S criv ete u n p rogram m a gra fico c h e d iseg n i il vostro n o m e in rosso, all’in te r n o
di u n retta n g o lo blu. P rogettate la classe NameViewer e la classe NameComponent.
Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
132 C apitolo 3
R 3 . 2 . C o s ’è r in ca p su la m e n to ? P erch é è utile?
R 3 . 7 . La variab ile di esem p lare value della classe Counter p u ò essere isp ezion ata m e d ia n te il m e to d o
d ’a ccesso getValue. D o v r e b b e essere p resen te u n m e to d o setValue p er m o d ifica re tale variabile?
M o tiv a te la risposta.
R 3 .8 .
a. S p ie g a te p e r c h é il c o str u tto r e BankAccount(double initialBalanee) n o n è str etta m e n te n e c essa rio .
C o n s e g u e n t e m e n t e , se lo t o g lia m o d a ll’in te r fa c c ia p u b b lic a , c o m e si p u ò o t t e n e r e u n
o g g e t t o di tip o BankAeeount c o n il sa ld o in iz ia le v o lu to ?
b. A l c o n t r a r io , p o t r e m m o t o g lie r e il c o s t r u t t o r e Ba nk Ac co un t ( ) e m e tt e r e a d is p o s i z io n e
s o lta n to BankAeeount(double initialBalanee)?
R3.21. Usando la tecnica di analisi descritta nel Paragrafo 3.5, studiate il programma presentato
nella sezione Esempi completi 3.1.
R3.22. Progettate una modifica della classe BankAccount che la porti ad avere questo comportamento:
ogni mese, le prime cinque transazioni sono gratuite, mentre ciascuna delle successive comporta un
addebito di $1. Aggiungete un metodo che addebiti complessivamente tali commissioni alla fine di
ogni mese. Quali ulteriori variabili di esemplare vi servono? Usando la tecnica di analisi descritta
nel Paragrafo 3.5, studiate uno scenario che evidenzi come vengono calcolate le commissioni su
un arco temporale di due mesi.
R3.23 (grafica). Nell’ipotesi di voler estendere il programma visualizzatore di automobili visto
nel Paragrafo 3.8 in modo che mostri uno scenario urbano, con diverse automobili e case, di quali
classi avreste bisogno?
R3.24 (grafica). Spiegate per quale motivo nella classe CarComponent le invocazioni dei metodi
getWidth e getHeight non hanno parametri espliciti.
R3.25 (grafica). Come modifichereste la classe Car per poter visualizzare automobili di dimensioni
diverse?
Esercizi di programmazione
^ E3.1. Vogliamo aggiungere al conta-persone visto nel Paragrafo 3.1 un pulsante che consenta
di annullare il conteggio di una persona avvenuto per sbaglio (operazione “annulla” o, in inglese,
undo). Progettate il metodo
public void undo()
che simuli l’effetto di tale nuovo pulsante. A titolo precauzionale, garantite che non ci siano effetti
anomali nel caso in cui la pressione del pulsante “annulla” sia più frequente di quella del pulsante
di conteggio {suggerimento: l’invocazione Math.max(n, o) restituisce il valore di n se n è maggiore di
zero, altrimenti restituisce zero).
^ E3.2. Dovete simulare !’utilizzo di un conta-persone in uno scenario in cui il numero di persone
che possono entrare in un luogo è limitato. Per prima cosa si imposta il limite massimo di persone,
invocando il metodo
public void setLimit(int maximum)
Se il pulsante di conteggio viene premuto dopo il superamento del limite impostato, non ha alcun
effetto sul conteggio memorizzato nel dispositivo {suggerimento: l’invocazione Math.min(n, limit)
restituisce il valore di n se n è minore di limit, altrimenti restituisce limit).
** E3.3. Simulate un circuito elettrico che controlla la lampada di un corridoio, con interruttori alle
due estremità del corridoio stesso. Ciascun interruttore può trovarsi in una di due posizioni (su o
giù, U p O down) e la lampada può essere accesa o spenta {on o ojj). lì cambiamento di stato di uno
qualsiasi dei due interruttori provoca il cambiamento di stato della lampada. Progettate i metodi:
public int getFirstSwitchStateO // ispeziona il primo interruttore, 0 = giù, l = su
public int getSecondSwitchStateO H ispeziona il secondo interruttore
public int getLampStateO // ispeziona la lampada, 0 = spenta, l = accesa
public void toggleFirstSwitchO // cambia lo stato del primo interruttore
public void toggleSecondSwitchO // cambia lo stato del secondo interruttore
136 C apitolo 3
E3.4 (collaudo). Scrivete la classe CircuitTester che collaudi tutte le possibili combinazioni di
interruttori previste dall’esercizio precedente, visualizzando lo stato previsto e effettivo della lampada
e di ciascun interruttore.
E3.5. Modificate l’interfaccia pubblica della classe dell’esercizio E3.3, che rappresenta un circuito
elettrico, in modo che metta a disposizione i metodi seguenti:
public int getSwitchState(int switch) // ispeziona un interruttore
public int get LampStateO
public void toggleSwitch(int switch) // cambia lo stato di un interruttore
Risolvete il problema usando soltanto le caratteristiche del linguaggio che sono state presentate
fino ad ora: l’aspetto complicato del progetto è l’individuazione di una rappresentazione dei dati
che consenta di ricostruire lo stato in cui si trovano i due interruttori.
E3.6 (collaudo). Scrivete la classe BankAccountTester il cui metodo main costruisca un conto
bancario, effettui un versamento di $1000 seguito dai prelievi di $500 e $400 e, infine, visualizzi
il saldo rimanente, seg;uito dal suo valore previsto.
E3.7. Aggiungete alla classe BankAccount un metodo
public void addlnterest(double rate)
che aggiunga al saldo del conto gli interessi, calcolati con il tasso fornito come parametro. Ad
esempio, dopo l’esecuzione di questi enunciati
BankAccount momsSavings = new BankAccount(IOOO);
momsSavings.addlnterest(lO); // interessi al 10%
il saldo di momsSavings è $1100. Progettate anche la classe BankAccountTester che visualizzi il saldo
finale e il suo valore previsto.
E3.8. Scrivete la classe SavingsAccount {conto di risparmio), del tutto simile alla classe BankAccount,
tranne per la presenza di un’ulteriore variabile di esemplare, interest. Fornite un costruttore che
assegni un valore sia al saldo iniziale sia al tasso di interesse. Fornite anche un metodo, addinterest
(privo di parametri espliciti), che accrediti sul conto gli interessi maturati, calcolati applicando il
tasso d’interesse interest al saldo del conto nel momento in cui il metodo viene invocato. Scrivete,
poi, la classe SavingsAccountTester che costruisca uno di tali “conti di risparmio” con saldo iniziale
di $1000 e tasso di interesse del 10%. Applicate, infine, per cinque volte il metodo addinterest,
visualizzando il saldo finale e il suo valore previsto, dopo averlo calcolato a mano.
E3.9. Aggiungete alla classe CashRegister il metodo printReeeipt che visualizzi i prezzi di ciascun
articolo acquistato e l’importo totale dovuto dal cliente. Suggerimento: dovete costruire una stringa
che contenga tutte le informazioni, usando il metodo concat della classe String per aggiungervi
i singoli articoli, uno dopo l’altro; per trasformare un prezzo in una stringa usate l’invocazione
String.valueOf(prezzo).
E3.10. Dopo la chiusura, il gestore di un negozio vorrebbe conoscere il volume totale di vendite
effettuate nella giornata: modificate la classe CashRegister in modo che sia in grado di farlo, dotandola
dei metodi aggiuntivi getSalesTotal e getSalesCount,che restituiscono, rispettivamente, l’incasso totale
e il numero totale di scontrini emessi, oltre al metodo reset che azzera tutti i contatori utilizzati,
in modo che le operazioni funzionino correttamente il giorno successivo.
E3.11. Realizzate la classe Employee (dipendente). Ogni dipendente ha un nome (una stringa) e uno
stipendio (di tipo double). Scrivete un costruttore con due parametri:
public Etnployee(String employeeName, double currentSalary)
R ealizzare classi 137
e i metodi:
public String getName()
public double getSalaryO
public void raiseSalary(double byPercent)
Tali metodi forniscono, nell’ordine, il nome e lo stipendio del dipendente e ne aumentano il salario
della percentuale indicata. Ecco un esempio di utilizzo:
Employee harry = new Employee("Hacker, Harry", 50000);
harry.raiseSaIary(io); // Harry ottiene un aumento di stipendio del 10%
E3.12. Realizzate la classe Car (automobile),con queste proprietà. U n ’automobile ha una determinata
resa del carburante (misurata in miglia/gallone o in litri/chilometro, a vostra scelta) e una certa
quantità di carburante nel serbatoio. La resa è specificata nel costruttore e inizialmente il serbatoio è
vuoto. Progettate: un metodo drive per simulare il percorso di un’automobile per una data distanza,
riducendo conseguentemente il livello di carburante nel suo serbatoio; un metodo getCasInTank,
per ispezionare il livello del carburante nel serbatoio; un metodo addCas, per fare rifornimento.
Ecco un esempio di utilizzo:
Car myHybrid = new Car(SO); // 50 miglia per gallone
myHybrid.addCas(20); // aggiungi 20 galloni di carburante
myHybrid.drive(lOO); // viaggia per 100 miglia
double gasLeft = myHybrid.getCasInTank()); // carburante rimasto
Potete ipotizzare che il metodo drive non venga mai invocato per una distanza maggiore di quella
percorribile con il carburante disponibile. Progettate anche la classe CarTester che collaudi tutti i
metodi.
E3.13. Realizzate la classe Product {prodotto). Ciascun prodotto ha un nome e un prezzo, descritti
nel costruttore: new Product("Toaster", 29-95). Progettate i seguenti metodi: getName, per conoscere il
nome del prodotto; getPrice, per conoscerne il prezzo; reducePrice, per ridurne il prezzo. Scrivete
un programma che crei due prodotti e ne visualizzi il nome e il prezzo, per poi ridurne i prezzi
di $5.00 e visualizzarli nuovamente.
E3.14. Realizzate una classe che impagini una semplice lettera. 11costruttore riceve come parametri
il nome del mittente (from) e quello del destinatario (to):
public Letter(String from. String to)
Progettate un metodo per aggiungere una riga di testo al contenuto della lettera, in fondo:
Dear destinatario:
riga vuota
prima riga del contenuto della lettera
seconda riga del contenuto della lettera
138 C apitolo 3
Dear Dohn :
Sincerely,
Mary
In esso, costruite un esemplare della classe Letter e invocate due volte il m eto d o addLine.
body = body.concat("Sincerely,").concat("\n");
e dei m e t o d i :
E c c o u n e se m p io di u tilizzo:
La classe BugTester deve costruire un insetto, farlo m uovere e girare alcune volte, poi visualizzarne
la posizion e effettiva e quella prevista.
E 3 .1 6 . R e a liz za te una classe Moth c h e rappresenti u n a falena c h e si sposta lu n g o una lin ea retta,
r ico rd a n d o la propria p o s iz io n e e la distanza da u n ’o r ig in e prefissata. Q u a n d o si sposta v erso una
so r g e n te lu m in o sa , la sua n u ova p o s iz io n e v ie n e a trovarsi a m età strada tra q u ella p r e ce d e n te e la
p o s iz io n e della so r g e n te lu m in o sa . D o ta te la classe di u n costru ttore:
e dei m e t o d i :
La classe MothTester deve costruire una falena, farla m uovere verso un paio di sorgenti lum inose,
poi visualizzarne la posizione effettiva e quella prevista.
E 3 .1 7 ( g r a f i c a ) . S criv ete un p rogram m a c h e riem p ia una finestra c o n una gran d e ellisse, aven te
il c o n to r n o n ero e la su p erficie in tern a d el c o lo r e c h e p referite. L’ellisse d ev e avere la finestra
c o m e r etta n g o lo di d e lim ita z io n e , a n c h e se qu esta v ie n e rid im en sion ata: all’in te r n o d el m e to d o
paintComponent in v o ca te i m e to d i getWidth e getHeight della classe DComponent.
E3.19 (grafica). S criv ete u n p rogram m a c h e tracci lo sc h iz z o di una casa, se m p lic e c o m e la figura
q u i riportata o p p u re p iù elaborata, se p referite (p o te te utilizzare una vista p rosp ettica, disegnare un
gratta cielo, c o lo n n e di m a r m o n e ll’in gresso o qualsiasi cosa). P rogettare la classe House e dotatela
d el m e to d o draw(Graphics2D g2) c h e d ise g n i la casa.
N o m e d el p o n te C a m p a ta m a g g io r e
( m i s u r a t a in p i e d i )
G o ld e n G ate 4200
B r o o k ly n 15 9 5
D elaw are M em o r ia l 2150
M a ck in ac 3800
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
T ipi di dati fondamentali 185
pow add
round multiply
sin subtract
sqrt java.math.BigInteger
tan add
toDegrees multiply
toRadians subtract
java.lang.String java.util.Scanner
charAt next
length nextDouble
substring nextint
java.lang.System javax.swing.IOptionPane
in showInputDialog
java.math.BigDecimal showMessageDialog
int mystery = 1;
mystery = 1 - 2 * mystery;
mystery = mystery + 1;
int mystery = l;
mystery = mystery + 1;
int mystery = 1 - 2 * mystery;
^ 2
5 = 5q +V^)t+-gt
G = 4n^
{m^ + m2)
\ INT
FV = PV- 1 + ------
100.
C = —2aècosy
186 C apitolo 4
In quali situ a zio n i si o ttie n e u n risu ltato sbagliato? C o m e si p u ò c o rreg g ere l’errore senza usare il
m e to d o Math.floorMod d e sc r itto nella s e z io n e N o t e p er Java 8 4.1?
R 4 .8 . Q u a li s o n o i valori d e lle se g u e n ti espressioni? Per ciascu n a esp ression e usate q u esti valori:
double X = 2.5;
double y = -1.5;
int m = 18;
int n = 4;
a. x + n * y - ( x + n)*y
b. m/n + m%n
c. 5 * x - n / 5
d. 1 - (1 - (1 - (1 - (1 - n))))
e. Math.sqrt(Math.sqrt(n))
a. n / 10 + n % 10
b. n%2 +m%2
c. (m + n) / 2
d. (m + n) / 2.0
e. (int) (0.5 * (m + n))
f. (int) Math.round(0.5 * (m + n))
R 4 . 1 0 . Q u a li s o n o i valori d e lle se g u e n ti espressioni? Per ciascu na esp ression e usate q u esti valori:
String S = "Hello";
String t = "World";
a. s.length0 + t.length()
b. s.substring(l, 2)
c. s.substring(s.length() / 2, s.length())
d. S + t
e. t + S
}
R4.12. Individuate almeno tre errori di esecuzione nel programma seguente.
public class HasErrors
{
public static void main(String[] args)
{
int X = O;
int y = 0;
Scanner in = new Scanner("System.in");
System.out.print("Please enter an integer:");
X = in.readIntO;
System.out.print("Please enter another integer: ");
X = in.readIntO;
System.out.println("The sum is " + x + y);
}
}
R4.13. Esaminate il codice seguente:
CashRegister register = new CashRegister();
register.recordPurchase(l9.93);
register.receivePayment(20, 0, 0, 0, 0);
System.out.print("Change: ");
System.out.printIn(register.giveChange());
Il programma visualizza il resto come 0.07000000000000028: spiegate perché. Date consigli per mi
gliorare il programma in modo che gli utenti non rimangano perplessi.
* R4.14. Spiegate la differenza fra 2,2.0, '2', "2" e "2.0".
* R4.15. Spiegate cosa calcolano i due seguenti frammenti di programma:
a. X = 2;
y = X + x;
b. S= "2";
t = S + s;
R4.16. Scrivete pseudocodice per un programma che legga una parola e ne visualizzi il primo
carattere, !’ultimo carattere e i caratteri che si trovano tra i due estremi. Ad esempio, se la parola
acquisita in ingresso fosse Harry, il programma dovrebbe visualizzare H y arr.
R4.17. Scrivete pseudocodice per un programma che legga un nome (come Harold lames Morgan)
e visualizzi un monogramma costituito dalle lettere iniziali del primo nome, del secondo nome e
del cognome (ad esempio HIM).
R4.18. Scrivete pseudocodice per un programma che calcoli la prima e !’ultima cifra di un numero.
Ad esempio, se il numero è 23456, il programma deve visualizzare 2 e 6. Su^erimento: usate %e
Math.loglO.
R4.19. Modificate lo pseudocodice del programma sviluppato nella sezione Consigli pratici 4.1
in modo che fornisca il resto usando monete da un quarto di dollaro (quarter), da dieci centesimi
188 C apitolo 4
★ ★ ★ R 4 .2 1 . N e l l ’ip otesi c h e u n ’antica civiltà abbia c o stru ito piram idi a base circolare (c io è c o n i), scrivete
p s e u d o c o d ic e p er un p rogram m a c h e n e d e te r m in i l ’area su p erficiale a partire da m isu ra zio n i c h e
si p ossan o effettuare dal su o lo .
, 2 ^ /r'
A = -c h + —
3 2c
Verificate la correttezza di questo pseudocodice usando il valore 4 come indice del giorno.Tracciate
uno schema della stringa che viene calcolata, in analogia con la Figura 3.
R 4 .2 5 . Lo pseudocodice seguente descrive come scambiare due lettere in una parola.
Dati: la stringa str e due posizioni, i e j (i precede j)
first = sottostringa di str dallìnizio fino alla posizione che precede i
charl = sottostringa di str contenente il solo carattere in posizione i
middle = sottostringa di str da i + 1 a j - 1
charJ = sottostringa di str contenente il solo carattere in posizione]
last = SOttOStringa di str da j + 1 fino alla fine della stringa
Concatenare, nell'ordine: first, charJ, middle, charl, last
La somma
La differenza
Il prodotto
Il valore medio
La distanza (cioè il valore assoluto della differenza)
Il valore massimo (cioè il valore più grande dei due)
Il valore minimo (cioè il valore più piccolo dei due)
E4.6. Scrivete un programma che chieda all’utente una misura in metri e che poi la converta in
miglia, piedi e pollici.
E4.7. Scrivete un programma che chieda all’utente la lunghezza del raggio e visualizzi:
• L’area e la circonferenza di un cerchio avente tale raggio
• Il volume e l’area superficiale di una sfera avente tale raggio
E4.8. Scrivete un programma che chieda all’utente le lunghezze dei lati di un rettangolo e visua
lizzi:
• L’area e il perimetro del rettangolo
• La lunghezza della sua diagonale (usando il teorema di Pitagora)
E4.9. Migliorate il programma presentato nella sezione Consigli pratici 4.1 in modo che consenta
all’utente di introdurre anche monete da un quarto di dollaro e non soltanto banconote.
E4.10. Scrivete un programma che chieda all’utente di fornire:
• Il numero di galloni di carburante presenti nel serbatoio di un’automobile
• L’efficienza del motore in miglia percorse per gallone
• Il prezzo del carburante (in dollari al gallone)
Poi, visualizzate la spesa necessaria per percorrere 100 miglia e la distanza percorribile con il car
burante rimasto ancora nel serbatoio.
E4.11. Modificate la classe Menu vista nella sezione Esempi Completi 3.1 in modo che le opzioni
del menu abbiano come etichette A, B, C e così via. Suggerimento: costruite una stringa con le
etichette.
E4.12. Nomi di file e loro estensioni. Scrivete un programma che chieda all’utente di fornire la lettera
che identifica il disco su cui si trova un file (ad esempio C), il percorso da seguire per trovarlo nel
file system (ad esempio \Windows\System), il nome del file (come Readme) e la sua estensione (txt).
Poi, visualizzate il nome completo del file, che in questo esempio è C:\Windows\System\Readme.txt
T ipi di dati fondamentali 191
E 4 .1 5 . V i s u a l i z z a r e u n a scacch iera. S criv ete u n p rogram m a c h e visu alizzi una scacch iera p er g io ca re
a tris (in in g lese, tic - ta c - to e ) , c o m e questa:
I I I I
+— +— +— +
I I I I
+— +— +— +
I I I I
+—+—+—+
P otreste o v v ia m e n te riso lv e re il p rob lem a in m o d o banale, sc r iv e n d o sette e n u n cia ti di q u esto tipo:
1 6 3 8 4
Fate attenzione alla gestione dei casi in cui il primo orario è inferiore al secondo:
Please enter the first time: 1730
Please enter the second time: 0900
15 hours 3 0 minutes
E4.18. Scrivere lettere giganti. Una lettera Hgigante si può visualizzare in questo modo:
* ♦
* ♦
(ricordate che la sequenza di escape \n rappresenta il carattere newline, dopo il quale i caratteri
verranno scritti a partire da una riga nuova). Definite stringhe simili per le lettere E, L e 0. Poi,
scrivete il seguente messaggio con lettere giganti:
H
E
L
L
0
E4.19. Scrivete un programma che traduca i numeri da 1 a 12 nei nomi dei mesi corrispondenti,
in inglese: January, February, March, ..., December. Suggerimento. Create una stringa molto lunga che
contenga i nomi di tutti i mesi ("January FebruaryMarch..."), nella quale inserirete spazi in modo
che ciascun nome di mese abbia la stessa lunghezza. Poi, usate il metodo substring per estrarre da
questa i soli caratteri del mese richiesto.
E4.20. Scrivete un programma che visualizzi un albero di Natale, come questo (ricordatevi di
usare le sequenze di escape):
/\
/ \
/ \
/ \
System.out.printIn("Expected: 0.13");
}
}
** E4.22. Realizzate la classe IceCreamCone (cono gelato) con i metodi getSurfaceArea() e getVolume().
Nel costruttore specificate l’altezza e il raggio del cono. Fate attenzione nell’utilizzare la formula
che calcola l’area superficiale: dovreste considerare solamente l’area laterale del cono, che è aperto
in cima per contenere il gelato.
E4.23. Realizzate la classe SodaCan {lattina di bibita) il cui costruttore riceve l’altezza e il diametro
della lattina. Progettate i metodi getVolume e getSurfaceArea, nonché la classe SodaCanTester per il
suo collaudo.
★★★ E 4 .2 4.Realizzate la classe Balloon che rappresenti il modello di un pallone di forma sferica riempito
d’aria, il cui costruttore costruisce un pallone vuoto. Dotatela di questi metodi:
• void addAir(double amount) aggiunge la quantità d’aria specificata
• double getVolumeO restitu isce il v o lu m e attuale
• double getSurfaceArea 0 restituisce l ’area su p erficiale attuale
• double getRadiusO restituisce il raggio attuale
Progettate, poi, la classe di collaudo BalloonTester che costruisca un pallone, vi aggiunga 100 cm^
d’aria, collaudi i tre metodi di accesso, aggiunga altri 100 cm^ di aria e collaudi nuovamente i tre
metodi di accesso.
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
246 C apitolo 5
• La le g g e di D e M organ in d ica c o m e sem p lificare espressioni in cu i l’op eratore n o t sia app licato
a esp ression i a n d o or.
,Esercizi di riepilogo
* R 5 .1 . Q u a l è il valore di ciascu n a variab ile d o p o l’e s e c u z io n e d e ll’en u n c ia to if?
S = O
if (x > O) { S++; }
if (y > 0) { S++; }
S = O
if (x > O) { S + + ; }
else if (y > 0) { s++; }
c. if (x = l) { y++; }
d. X = in.nextIntO;
if (in.hasNextIntO)
D ecisioni 247
{
sum = sum + x;
}
else
{
System.out.println("Bad input for x”);
x");
}
String IetterGrade = "F";
if (grade >« 90) { IetterGrade ••A"; )
if (grade >* 80) { IetterGrade ••B"; }
if (grade >■ 70) { IetterGrade ”C"; }
if (grade >* 60) { IetterGrade "D"; }
R5.5. Nell’ipotesi che x e y siano variabili di tipo double, scrivete un frammento di codice che
assegni a y il valore di x se questo è positivo, altrimenti 0.
R5.6. Nell’ipotesi che x e y siano variabili di tipo double, scrivete un frammento di codice che
assegni a y il valore assoluto di x senza invocare la funzione Math.abs. Usate un enunciato if.
R5.7. Spiegate perché è più difficile confrontare numeri in virgola mobile piuttosto che numeri
interi. Scrivete un frammento di codice che verifichi se una variabile intera n è uguale a 10 e un
altro che verifichi se una variabile in virgola mobile x è circa uguale a 10.
R5.8. Dati due pixel sullo schermo aventi come coordinate (Xj, y,) e (^2»y2) (tutti numeri interi),
scrivete frammenti di codice che verifichino se:
a. Si tratta dello stesso pixel.
b. Sono due pixel molto vicini, con distanza minore di 5.
R5.9. È facile confondere gli operatori = e ==. Scrivete un programma di prova che contenga
fenunciato:
if (floor = 13)
Quale messaggio d’errore viene visualizzato? Scrivete un altro programma di prova che contenga
l’enunciato:
248 C apitolo 5
count == 0;
R5.10. Ogni casella di una scacchiera può essere individuata da una lettera e un numero, come,
in questo esempio, la casella g5:
Lo pseudocodice seguente descrive un algoritmo che determina se una casella è nera o bianca,
dato il suo identificativo (lettera e numero).
Se la lettera è a, c, e oppure g
Se il numero è dispari
colore = "n ero "
Altrimenti
colore = "bianco"
Altrim enti
Se il numero è pari
colore = "n ero "
Altrimenti
colore= "bianco"
Usando la procedura delineata nella sezione Suggerimenti per la programmazione 5.5, seguite
passo dopo passo l’esecuzione del codice nel caso g5.
R5.11 (collaudo). Progettate un insieme di quattro casi di prova per l’algoritmo dell’esercizio
precedente, in modo da coprire tutte le diramazioni.
R5.12. In un programma di calendarizzazione {scheduling) di eventi si deve verificare se due
appuntamenti si sovrappongono. Per semplicità, ipotizziamo che gli appuntamenti inizino sempre
a un’ora esatta (senza minuti) e usiamo l’orario militare (cioè con le ore che vanno da 0 a 23).
Lo pseudocodice seguente descrive un algoritmo che determina se l’appuntamento che inizia
all’ora s ta rti e termina all’ora endl si sovrappone all’appuntamento che inizia all’ora start2 e termina
all’ora end2.
Se sta rti > start2
S = sta rti
Altrim enti
S = start2
D ecisioni 249
S cendi < e n d 2
e = endl
Altrimenti
e = end2
Ses < e
Gli appuntamenti si sovrappongono.
Altrimenti
Gli appuntamenti non si sovrappongono.
Seguite passo dopo passo l’esecuzione del codice con gli appuntamenti 10-12 e 11-13, poi con
gli appuntamenti 10-11 e 12-13.
^ R5.13. Disegnate il diagramma di flusso per l’algoritmo dell’esercizio precedente.
* R5.14. Disegnate il diagramma di flusso per l’algoritmo dell’Esercizio E5.14.
* R5.15. Disegnate il diagramma di flusso per l’algoritmo dell’Esercizio E5.15.
** R5.16(collaudo). Progettate un insieme di casi di prova per l’algoritmo dell’Esercizio R5.12.
R5.17 (collaudo). Progettate un insieme di casi di prova per l’algoritmo dell’Esercizio E5.15.
** R5.18. Scrivete lo pseudocodice per un programma che chiede all’utente un mese e un giorno
e visualizza un messaggio opportuno se si tratta di una di queste festività statunitensi:
• New Year’s Day (I gennaio)
• Independence Day (4 luglio)
• Veterans Day (I I novembre)
• Christmas Day (25 dicembre)
R5.19. Scrivete lo pseudocodice per un programma che assegna un voto in lettere al risultato di
un questionario, secondo la tabella seguente:
Punteggio Voto
90-100 A
80-89 B
70-79 C
60-69 D
< 60 F
R5.20. Illustrate le differenze tra l’ordinamento lessicografico delle stringhe in Java e l’ordinamento
delle parole in un dizionario o in un elenco telefonico. Suggerimento: considerate stringhe come
IBM, wiley.com, Century 21, While-U-Wait.
R5.21. In ciascuna delle seguenti coppie, quale stringa precede l’altra nell’ordinamento lessico
grafico?
a. "Tom", "Derry"
b. "Tom", "Tornato"
c. "church", "Churchill"
d. "car manufacturer", "carburetor"
e. "Harry", "hairy"
f "Dava", " Car"
g. "Tom", "Tom"
h. "Car", "Carl"
i. "car", "bar"
250 C apitolo 5
altre 5 combinazioni
★★★ E vero O è falso che d &&B assume lo stesso valore di B && d per qualsiasi espressione
R 5 .2 8 .
booleana Ae B?
* La funzione di “ricerca avanzata” di molti motori di ricerca vi consente di utilizzare
R 5 .2 9 .
operatori booleani per comporre interrogazioni complesse, come “(cats OR dogs) AND NOT
pets”. Mettete a confronto questi operatori di ricerca con gli operatori booleani di Java.
a. b && X == 0
b. b II X == 0
c. !b&& X = = 0
d. !b II X == 0
e. b && X ! = 0
f b II X ! = 0
g- !b && X != 0
h. !b II X != 0
R 5 .3 2 . Semplificate gli enunciati seguenti, dove b è una variabile di tipo boolean e n è una variabile
di tipo int.
S u g g e r im e n to : qual è il valore di n == 0?
b. if (n == 0) { b = false; } else { b = true; }
c. b = false; if (n > l) { if (n < 2) { b = true; } }
d. if (n < l) { b = true; } else { b = n > 2; }
«it>E£H»«MUrCU
Esercizi di programmazione
ES.I. Scrivete un programma che acquisisca un numero intero e visualizzi un messaggio diverso
nei casi in cui il numero acquisito sia negativo, uguale a zero o positivo.
ES.2. Scrivete un programma che acquisisca un numero in virgola mobile e visualizzi il messaggio
“zero” se è uguale a zero, altrimenti visualizzi il messaggio “positive” (se è positivo) o “negative”
(se è negativo). Aggiungete il messaggio “small” se il valore assoluto del numero è inferiore a 1
oppure “large” se è superiore a un milione.
ES.3. Scrivete un programma che legga un numero intero e visualizzi il numero delle sue cifre,
verificando prima se il numero è > 10, poi se è > 100 e così via (ipotizzando che tutti i possibili
numeri interi siano inferiori a dieci miliardi). Se il numero è negativo, moltiplicatelo prima per
- 1.
ES.4. Scrivete un programma che legga tre numeri e visualizzi il messaggio “all the same” se sono
tutti uguali,“all different” se sono tutti diversi e “neither” se non sono tutti uguali e non sono tutti
diversi.
ES.S. Scrivete un programma che legga tre numeri e visualizzi il messaggio “increasing” se sono in
ordine crescente,“decreasing” se sono in ordine decrescente e “neither” se non sono né in ordine
crescente né in ordine decrescente. In questo esercizio crescente significa strettamente crescente, cioè
ciascun valore deve essere maggiore del precedente (analogo significato ha il termine decrescente):
la sequenza 3 4 4, quindi, non va considerata crescente.
E5.6. Risolvete nuovamente l’esercizio precedente ma,prima di leggere i numeri, chiedete all’utente
se l’ordine crescente/decrescente va verificato in senso “stretto” oppure no. Nel secondo caso,
ovviamente, la sequenza 3 4 4 è considerata crescente, così come la sequenza 4 4 4, che è anche
decrescente.
252 C apitolo 5
ES.7. Scrivete un programma che legga tre numeri interi e visualizzi il messaggio “in order’’ se
sono ordinati in senso crescente o decrescente e “not in order’’in caso contrario. Ad esempio:
1 2 5 in order
1 5 2 not in order
5 2 1 in order
12 2 in order
ES.8. Scrivete un programma che legga quattro numeri interi e visualizzi il messaggio “two pairs”
se i dati acquisiti costituiscono due coppie (in qualsiasi ordine) e “not two pairs” in caso contrario.
Ad esempio;
12 21 two pairs
12 23 not two pairs
22 2 2 two pairs
ES.9. L’ago di una bussola indica il numero di gradi di deviazione rispetto al Nord, misurati in
senso orario. Scrivete un programma che legga il valore dell’angolo e visualizzi la direzione più
vicina, scelta tra una di quelle riportate solitamente nelle bussole; N, NE, E, SE, S, SW, W, NW
(dove le lettere N, E, S e W indicano i punti cardinali in senso orario; nord, est, sud, ovest). In caso
di equidistanza, va preferita una delle direzioni principali (N, E, S o W).
ES. 10 (economia). Scrivete un programma che legga il nome di un dipendente e la sua paga
oraria,ad esempio $9.25. Poi, deve chiedere il numero di ore lavorate dal dipendente nella settimana
precedente, accettando anche un numero frazionario. Infine, deve calcolare il pagamento dovuto e
visualizzare la busta paga del dipendente, tenendo presente che le ore di straordinario (cioè quelle
che eccedono le 40 ore) hanno una paga oraria pari al 150 per cento di quella normale. Progettate
la classe Paycheck {busta paga).
ES.ll. Scrivete un programma che legga un valore di temperatura seguito dalla lettera C per
Celsius O F per Fahrenheit, poi visualizzi un messaggio che dica se, al livello del mare e a quella
temperatura, l’acqua si trova allo stato solido, liquido o gassoso.
ES. 12. Il punto di ebollizione dell’acqua diminuisce di circa un grado Celsius ogni 300 metri (o
1000 piedi) di altitudine. Migliorate il programma dell’esercizio precedente, consentendo all’utente
di specificare l’altitudine, in metri o in piedi.
ES. 13. Aggiungete all’esercizio precedente la gestione degli errori: se l’utente non digita un
numero quando sarebbe previsto che lo facesse, oppure fornisce un’unità di misura non valida per
l’altitudine, visualizzate un messaggio d’errore e terminate il programma.
ES.14. Confrontando due istanti di tempo, ciascuno espresso come ora e minuti (usando la
consuetudine militare, cioè con le ore che vanno da 0 a 23), il codice seguente determina quale
istante preceda l’altro:
Se orai < ora2
istantel è il primo.
A ltrim enti se orai è uguale a ora2
Se m inutol < minuto2
istantel è il primo.
A ltrim enti se m in u to l è uguale a minuto2
istantel e istante2 sono identici.
Altrim enti
istante2 è il primo.
Altrim enti
istante2 è il primo.
D ecisioni 253
Scrivete un programma che chieda all’utente di fornire due istanti di tempo e li visualizzi in ordine
temporale, visualizzando cioè per primo quello che precede l’altro. Progettate la classe Time dotata
del metodo
public int compareTo(Time other)
che restituisca -1 se l’istante di tempo rappresentato dal parametro implicito precede quello
rappresentato dal parametro esplicito other e 1 nel caso opposto, restituendo 0 se i due istanti sono
identici.
E5.15. L’algoritmo seguente individua la stagione (Spring, Summer, Fall o Winter, cioè,
rispettivamente, primavera, estate, autunno o inverno) a cui appartiene una data, fornita come
mese e giorno, due numeri interi.
Se mese è 1, 2 o 3, stagione = "W in te r"
Altrim enti se mese è 4,5 o 6, stagione = "Spring"
Altrim enti se mese è 7,8 o 9, stagione = "Summer"
Altrim enti se mese è 10,11 o 12, stagione= "Fall"
Se mese è divisibile per 3 e giorno > = 21
Se stagione è "W inter"stagione = "Spring"
Altrim enti se stagione è "Spring" stagione = "Summer"
Altrim enti se stagione è "Summer" stagione ="F a ll"
Altrim enti stagione= "W inter"
Scrivete un programma che chieda all’utente un mese e un giorno e, poi, visualizzi la stagione
determinata da questo algoritmo. Progettate la classe Date dotata del metodo get Season.
ES. 16. Scrivete un programma che traduca un voto espresso in lettere nel corrispondente voto
numerico. 1voti in lettere sono A, B, C, D e F, eventualmente seguiti da un segno + o 1loro valori
numerici sono, nell’ordine, 4,3,2,1 e 0.1 voti F+ e F- non esistono. Un segno + aumenta il voto
numerico di 0.3, mentre un segno - lo diminuisce della stessa quantità. 11 voto A+ è comunque
uguale a 4.0.
Enter a letter grade: B-
The numeric value is 2.7.
Non esistevano regimi di tassazione distinti per persone coniugate e per persone non coniugate.
Scrivete un programma che calcoli l’importo della tassazione sul reddito secondo questo schema.
E5.19. Scrivete un programma che acquisisca dall’utente la descrizione di una carta da gioco.
usando la seguente notazione abbreviata:
A Ace (asso)
2 . . . 10 Valore della carta
D Jack (fante)
Q Queen (donna)
K King (re)
D Diamonds (quadri)
H Hearts (cuori)
S Spades (picche)
C Clubs (fiori)
Progettate la classe Card il cui costruttore riceva come parametro la stringa che descrive la carta e il
cui metodo getDescription restituisca una stringa che descrive la carta, come specificato. Se la stringa
fornita come parametro di costruzione non rispetta il formato richiesto, il metodo getDescription
deve restituire la stringa "Unknown".
ES.20. Scrivete un programma che legga tre numeri in virgola mobile e visualizzi il maggiore di
essi. Ad esempio:
Please enter three numbers: 4 9 2.5
The largest number is 9.
ES.21. Scrivete un programma che legga tre stringhe e le visualizzi in ordine lessicografico. Ad
esempio:
Enter three strings: Charlie Able Baker
Able
Baker
Charlie
ES.22. Scrivete un programma che legga due numeri in virgola mobile e verifichi se sono uguali
quando vengono arrotondati alla seconda cifra decimale. Ecco due esempi di esecuzione:
Enter two floating-point numbers: 2.0 1.99998
They are the same up to two decimal places.
Enter two floating-point numbers: 2.0 1.98999
They are different.
ES.23. Scrivete un programma che chieda all’utente di fornire una singola lettera dell’alfabeto,
visualizzando poi il messaggio “Vowel” se si tratta di una vocale e “Consonant” se si tratta di una
consonante. Se l’utente non digita una lettera (cioè un carattere compreso tra “a” e “z” oppure
tra “A” e “Z”) oppure digita una stringa di lunghezza maggiore di uno, visualizzate un messaggio
d’errore.
ES.24. Scrivete un programma che chieda all’utente di digitare il numero identificativo di un
mese (1 per gennaio, 2 per febbraio, e così via), per poi visualizzare il numero di giorni da cui è
composto il mese selezionato. Nel caso di febbraio, visualizzate il messaggio “28 days”.
D ecisioni 255
Enter a month: 5
30 days
public in t getLengthO
Non usate un diverso ramo if/else per ciascun mese, ma componete le condizioni usando gli
opportuni operatori booleani.
E5.25 (economia). Un supermercato premia i propri clienti con buoni spesa il cui importo
dipende dalla quantità di denaro spesa in prodotti alimentari (groceries). Ad esempio, spendendo
50 dollari si ottiene un buono spesa di importo pari all’otto percento di quella somma. La tabella
seguente mostra la percentuale usata per calcolare il buono spesa relativo a somme diverse. Scrivete
un programma che calcoli e visualizzi il valore del buono da consegnare al cliente, sulla base della
somma di denaro che ha speso nell’acquisto di prodotti alimentari.
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
Iterazioni 327
^ R6.6. Scrivete le tabelle relative al tracciamento a mano dell’esecuzione dei cicli seguenti:
a. int i = 0; int j = 10; int n = 0;
while (i < j) { i++; j--; n++; }
b. int i = 0; int j = 0; int n = 0;
while (i <io) { i++; n = n + i + j; j++; }
c. int i = 10; int j = 0; int n = 0;
while (i > O) { i--; j++; n = n + i - j ; }
d. int i = 0; int j = 10; int n = 0;
while (i != j) { i = i + 2; j = j - 2; n++; }
^ R6.8. Cos’è un ciclo infinito? Nel vostro computer, in che modo potete terminare un programma
che sta eseguendo un ciclo infinito?
Iterazioni 329
* Scrivete lo pseudocodice per un programma che visualizzi una tabella di conversione tra
R 6 .1 5 .
gradi Celsius e Fahrenheit come questa:
Celsius I Fahrenheit
0I 32
10 I 50
20 I 68
100 I 212
Scrivete lo pseudocodice per un programma che legga i dati di uno studente (nome e
R 6 .1 6 .
cognome, seguiti da una sequenza di punteggi di valutazione e dal valore sentinella -1), per poi
visualizzare il suo punteggio medio. Poi, tenendone traccia su carta, eseguite a mano il programma
con questi dati di ingresso:
Harry Morgan 94 71 86 95 -1
Scrivete lo pseudocodice per un programma che legga i dati relativi a una sequenza di
R 6 .1 7 .
studenti, per poi visualizzare il punteggio totale di ciascuno. I dati relativi a uno studente sono il
nome e il cognome, seguiti da una sequenza di punteggi di valutazione e dal valore sentinella —1.
La sequenza di studenti viene terminata dalla parola END. Ecco un esempio di sequenza di dati in
ingresso:
330 C apitolo 6
Harry Morgan 94 71 86 95 -I
Sally Lin 99 98 100 95 90 -1
END
Successivamente, tenendone traccia su carta, eseguite a mano il programma con tali dati di ingresso.
n;
}
c. int S = 1;
int n;
for (n = 1; n <= 5; n++)
{
S = S + n;
n-H-;
}
S y s te m .o u t.p rin t(s + " " n);
R6.22. Cosa visualizzano i frammenti di codice seguenti? Trovate la risposta senza usare il com
puter, eseguendoli a mano e tenendo traccia dell’esecuzione su carta.
a. int n = 1;
for (int i = 2; i < 5; i++) { n = n + i; }
System.out.print(n);
b. int i;
double n = 1 / 2;
for (i = 2; i < 5; i++) { n = n + l.O / i; }
System.out.print(i);
c. double X = 1;
double y = I;
int i = 0;
do
{
y = y / 2;
X = X + y;
i++;
}
while (x < 1.8);
System.out.print(i);
d. double x = l;
double y = 1;
int i = 0;
while (y >= 1.5)
{
X = X / 2;
y = X + y;
i++;
}
System.out.print(i);
R6.23. Fornite un esempio di ciclo for in cui i limiti simmetrici siano la scelta più naturale. Fornite
un altro esempio di ciclo for in cui siano preferibili limiti asimmetrici.
R6.24. Aggiungete allo storyboard del programma di conversione visto nel Paragrafo 6.6 una
scheda che descriva uno scenario in cui l’utente fornisce in ingresso unità di misura incompatibili.
R6.25. Nel Paragrafo 6.6 abbiamo deciso di mostrare all’utente, all’interno del prompt, un elenco
di tutte le unità di misura valide. Se il programma consentisse !’utilizzo di un numero di unità
di misura molto più elevato, questo approccio non sarebbe praticabile. Progettate una scheda,
da aggiungere allo storyboard, che illustri un approccio alternativo: se l’utente fornisce un’unità
sconosciuta, viene visualizzato un elenco di tutte le unità note al programma.
R6.26. Modificate lo storyboard del Paragrafo 6.6 in modo che mostri un menu per chiedere
all’utente se vuole convertire una misura, vedere le informazioni di ausilio all’utilizzo del pro-
332 C apitolo 6
gramma {program help) o terminare l’esecuzione. Il menu deve essere visualizzato all’inizio del
programma, dopo aver convertito una sequenza di valori e dopo aver visualizzato un messaggio
d’errore.
Disegnate il diagramma di flusso di un programma che effettui conversioni da un’unità di
R 6 .2 7 .
misura a un’altra, come descritto nel Paragrafo 6.6.
Nel Paragrafo 6.7.5, il codice che trova il minimo e il massimo valore tra quelli forniti in
R 6 .2 8 .
ingresso inizializza le variabili largest e sm allest usando il primo valore acquisito. Perché non si
possono inizializzare al valore zero?
R 6 .2 9 . Cosa sono i cicli annidati? Fornite un esempio in cui solitamente si usano cicli annidati.
R 6 .3 0 . Questi cicli annidati
for (in t i = 1; i <= height; i++)
{
for (int j = 1; j <= width; j++) { System.out.print("*"); }
System.out. println() ;
}
visualizzano un rettangolo di larghezza (width) e altezza (height) assegnate, come questo:
R6.34 (collaudo). Spiegate in dettaglio come usare il vostro debugger per esaminare le infor
mazioni contenute in un oggetto di tipo String.
R6.35 (collaudo). Spiegate in dettaglio come usare il vostro debugger per esaminare le infor
mazioni contenute in un oggetto di tipo Rectangle.
R6.36 (collaudo). Spiegate in dettaglio come usare il vostro debugger per esaminare il saldo
contenuto in un oggetto di tipo BankAccount.
R6.37 (collaudo). Spiegate la strategia “dividere per vincere”, utilizzata per avvicinarsi a un errore
usando il debugger.
Iterazioni 333
Esercizi di p r o a c a u u M i^
E6.1. Scrivete un programma che legga il saldo iniziale di un investimento e un tasso di interesse
annuo, per poi visualizzare il numero di anni necessari perché l’investimento raggiunga un saldo
uguale a un milione di dollari.
E6.2. Scrivete programmi che, usando cicli, calcolino:
a. la somma di tutti i numeri pari compresi tra 2 e 100 (estremi inclusi);
b. la somma di tutti i numeri compresi tra 1 e 100 (estremi inclusi) che siano quadrati
perfetti;
c. tutte le potenze di 2, da 2^^ a
d. la somma di tutti i numeri dispari compresi tra a e b (estremi inclusi), dove a e b sono
valori acquisiti in ingresso;
e. la somma di tutte le cifre dispari di un numero acquisito in ingresso (se, ad esempio, il
numero è 32677, la somma da calcolare 03 + 7 + 7 = 17).
E6.3. Scrivete programmi che leggano una sequenza di numeri interi e visualizzino:
a. il valore minimo e il valore massimo tra quelli acquisiti;
b. il numero di valori pari e il numero di valori dispari tra quelli acquisiti;
c. le somme parziali dei numeri acquisiti, calcolate e visualizzate dopo ogni acquisizione;
se, ad esempio, i valori in ingresso sono 1 7 2 9, il programma deve visualizzare 1, 8 (= 1
+ 7), 10 (= 1 + 7 + 2), 19 (= 1 + 7 + 2 + 9);
d. i valori adiacenti duplicati; se, ad esempio, i valori acquisiti sono 1 3 3 4 5 5 6 6 6 3, il
programma deve visualizzare 3 5 6.
E6.4. Scrivete programmi che leggano una riga di dati in ingresso sotto forma di stringa e visua
lizzino:
a. le sole lettere maiuscole della stringa;
b. a partire dalla seconda lettera della stringa, una lettera viene visualizzata e l’altra no, al
ternativamente;
c. la stringa con tutte le vocali sostituite da un carattere di sottolineatura (underscore);
d. il numero di vocali presenti nella stringa;
e. le posizioni di tutte le vocali presenti nella stringa.
E6.5. Completate il programma progettato nella sezione Consigli pratici 6.1, che legga dodici
valori di temperatura e visualizzi il numero corrispondente al mese con la temperatura più elevata.
E6.6. Scrivete un programma che (visualizzando un unico messaggio) chieda all’utente di fornire
in ingresso un insieme di valori in virgola mobile e, poi, visualizzi:
• la media dei valori;
• il valore minore;
• il valore maggiore;
• la dinamica dei valori (ratine), cioè la differenza tra il valore maggiore e il valore minore.
Il programma deve usare un’apposita classe DataSet per rappresentare l’insieme dei dati acquisiti,
dotata del metodo
per aggiungere un valore all’insieme, oltre ai metodi getAverage, getSm allest, getLargest e getRange,
che risolvono, nell’ordine, i quattro problemi posti.
Traducete in un programma Java la seguente descrizione in pseudocodice, che trova il valore
E 6 .7 .
minimo in un insieme di dati acquisiti in ingresso.
primo VaIore = True.
Finché è stato acquisito con successo un nuovo valore
Se primo valore è True
valore minimo = valore acquisito,
primo valore = False.
Altrimenti se valore acquisito < valore minimo
valore minimo = valore acquisito.
Visualizza valore minimo.
Per scambiare le lettere, costruite le tre sottostringhe (first, middle e la st) indicate nella figura.
fir s t middle D la st
Scrivete un programma che legga una parola e visualizzi ciascuno dei suoi caratteri da solo
E 6 .9 .
su una riga diversa, ordinatamente. Se, ad esempio, l’utente digita la stringa "Harry", il programma
deve visualizzare:
H
a
r
r
y
Scrivete un programma che legga una parola e la visualizzi al contrario. Se, ad esempio,
E 6 .1 0 .
l’utente digita la stringa "Harry", il programma deve visualizzare:
yrraH
Scrivete un programma che legga una parola e visualizzi il numero di vocali presenti in
E 6 .1 1 .
essa (per questo esercizio assumete che le vocali siano a e i o u y). Se, ad esempio, l’utente digita la
stringa "Harry", il programma deve visualizzare il messaggio: 2 vowels.
Scrivete un programma che legga una parola e visualizzi tutte le sue sottostringhe, ordinate per
E 6 .1 2 .
lunghezza crescente. Se, ad esempio, l’utente digita la stringa "rum", il programma deve visualizzare:
Iterazioni 335
r
U
m
ru
um
rum
^ E6.15. Usando la classe Picture presentata nella sezione Esempi completi 6.2, applicate !’effetto
“tramonto” (sunset) a un’immagine, aumentando in ciascun pixel il valore del rosso del 30 per
cento (senza superare il valore massimo, 255).
E6.16. Usando la classe Picture presentata nella sezione Esempi completi 6.2, applicate !’effetto
“telescopio” (telescope) a un’immagine, facendo diventare neri tutti i pixel che si trovano all’esterno
di un cerchio, il cui centro deve coincidere con il centro dell’immagine e il cui raggio deve essere
il 40 per cento della dimensione minore dell’immagine (larghezza o altezza).
E6.17. Scrivete un programma che visualizzi una tavola pitagorica, come questa:
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
10 20 30 40 50 60 70 80 90 100
E6.18. Scrivete un programma che legga un numero intero, n, e visualizzi, usando asterischi, un
quadrato pieno e uno con il solo contorno, uno di fianco all’altro, con il lato che misura n. Se, ad
esempio, l’utente fornisce il numero 5, il programma deve visualizzare:
:tc:tc:(c>|c:4c % «
* *
*****
336 C apitolo 6
E6.19. Scrivete un programma che legga un numero intero, n, e visualizzi, usando asterischi,
un rombo pieno il cui lato abbia lunghezza n. Se, ad esempio, l’utente fornisce il numero 4, il
programma deve visualizzare:
E6.20 (economia). Conversione di valuta. Scrivete un programma che per prima cosa chieda
all’utente di fornire il prezzo odierno di un dollaro statunitense in yen giapponesi, per poi leggere
ripetutamente valori in dollari statunitensi, convertendoli in yen, fino all’arrivo del valore sentinella,
che è uno zero.
E6.21 (economia). Scrivete un programma che per prima cosa chieda all’utente di fornire il
prezzo odierno di un dollaro statunitense in yen giapponesi, per poi leggere ripetutamente valori
in dollari statunitensi, convertendoli in yen, fino all’arrivo del valore sentinella, che è uno zero.
Successivamente, il programma legge una sequenza di valori in yen e li converte i dollari: anche
questa seconda sequenza viene terminata da uno zero.
** E6.22. Il dilemma di Monty Hall. In una rivista molto popolare, Marylin vos Savant descrisse il
problema seguente (ispirato a un gioco a premi televisivo presentato da Monty Hall).
Immaginate di essere i concorrenti di un gioco a premi televisivo nel quale dovete scegliere
una porta tra le tre che vi vengono proposte: dietro a una porta c’è, in premio, un’automobile,
dietro le altre ci sono delle capre.Voi scegliete una porta, ad esempio la numero 1, e il presentatore,
che sa cosa c’è dietro alle porte, apre una delle altre due, ad esempio la numero 3, dove c’è una
capra. A quel punto vi chiede:“Vuoi cambiare idea e scegliere la porta numero 2?”. Il problema è:
vi conviene cambiare idea?
La giornalista dimostrò che conviene, ma molti dei suoi lettori, tra i quali alcuni docenti di
matematica, non furono d’accordo con lei, sostenendo che la probabilità di vittoria non cambia
per il solo fatto che sia stata aperta una porta.
Il vostro compito è quello di simulare questo gioco. In ciascuna iterazione della simulazione
scegliete a caso una porta (cioè un numero da 1 a 3) dove posizionare l’automobile, poi simulate il
fatto che il concorrente scelga, di nuovo a caso, una porta. Successivamente, il presentatore del gioco
sceglie a caso una porta che nasconde una capra (ma che non sia la porta scelta dal concorrente).
Incrementate il contatore della “strategia 1” se il giocatore vince cambiando la porta scelta e il
contatore della “strategia 2” se il giocatore vince senza cambiare idea. Eseguite 1000 simulazioni
e visualizzate entrambi i contatori.
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
402 C apitolo 7
Il collaudo regressivo
• U n p a c c h e t to d i p ro v e {test suite) è u n in sie m e di prove p er il c o lla u d o rip etu to .
• Il c o lla u d o regressivo p reved e l ’e s e c u z io n e rip etu ta di prove già e se g u ite in p reced en za , per
essere certi c h e p rob lem i risolti in passato n o n co m p a ia n o n elle n u o v e version i del program m a.
java.lang.Boolean java.util.ArrayList<E>
java.lang.Double add
java.lang.Integer get
java.util.ArrayS remove
copyOf set
toString size
R 7 .3 . S criv ete un p rogram m a c h e c o n tie n e u n errore di lim iti ed e s e g u ite lo . C o sa accad e al vostro
co m p u ter?
a. 1 2 3 4 5 6 7 8 9 10
b. 0 2 4 6 8 10 12 14 16 18 2 0
c. 1 4 9 16 2 5 3 6 4 9 6 4 81 100
d. 0 0 0 0 0 0 0 0 0 0
A rray e vettori 403
e. 1 4 9 16 9 7 4 9 11
f. 0 10 10 10 10 1
g. 0 1 2 3 4 0 1 2 3 4
★★★ R7.8. Scrivete un ciclo che riempia !’array values con dieci numeri casuali compresi tra 1 e 100.
Poi, scrivete il codice per due cicli annidati che riempiano !’array values con dieci numeri casuali
compresi tra 1 e 100 ma tutti diversi tra loro (cioè, al termine, !’array non deve contenere numeri
replicati).
R7.9. Scrivete un ciclo che calcoli contemporaneamente il valore massimo e il valore minimo in
un array di numeri.
* R7.10. Cosa c’è di sbagliato in questi due frammenti di codice?
a. int[] values = new int[l0];
for (int i = 1; i <= 10; i++)
404 C apitolo 7
values[i] = i * i;
}
b. int[] values;
for (int i = 0; i < values.length; i++)
{
values[i] = i * i;
}
R7.11. Risolvete i problemi seguenti scrivendo cicli for estesi.
a. Visualizzare tutti gli elementi di un array in un’unica riga, separati da uno spazio.
b. Calcolare il valore massimo tra tutti gli elementi dell’array.
c. Contare quanti elementi dell’array sono negativi.
R7.12. Riscrivete i cicli seguenti senza usare il for esteso, tenendo presente che values è di tipo
double[].
R7.13. Riscrivete i cicli seguenti usando il for esteso, tenendo presente che values è di tipo dou-
ble[].
3| 5| 7 | u | l 3 l
/ / / / /
3| 5l 7 | n | l 3 l T |
1 2 5 5 3 1 2 4 3 2 2 2 2 3 6 5 5 6 3 1
406 C apitolo 7
R7.24. Cosa c’è di sbagliato nel metodo seguente, che vorrebbe riempire di numeri casuali un
array?
public void makeCofnbination(int[] values, int n)
{
Random generator = new Random();
int[] numbers = new int[values.length];
for (int i = 0; i < numbers.length; i++)
{
numbers[i] = generator.nextlnt(n);
}
values = numbers;
}
R7.25. Sono dati due array che riportano, rispettivamente, le coordinate x e y di un insieme di
punti nel piano cartesiano. Per disegnarli, dobbiamo conoscere le coordinate x e y dei vertici del
più piccolo rettangolo parallelo agli assi cartesiani che contenga tutti i punti. Come si possono
calcolare queste informazioni usando gli algoritmi elementari visti nel Paragrafo 7.3?
R7.26. Risolvete il problema della valutazione dei questionari posto nel Paragrafo 7.4 ordinando
prima !’array. Come va modificato l’algoritmo che calcola il punteggio totale?
R7.27. Risolvete il problema descritto nel Paragrafo 7.5 usando un algoritmo che elimini e inserisca
gli elementi invece di scambiarli. Descrivete l’algoritmo mediante pseudocodice, nell’ipotesi che
esistano metodi per la rimozione e per l’inserimento. Provate a eseguire concretamente l’algorit
mo usando delle monete e spiegate perché sia meno efficiente di quello che effettua gli scambi,
sviluppato nel Paragrafo 7.5.
R7.28. Progettate un algoritmo che trovi, in un array di numeri, il valore che ricorre più fre
quentemente. Disponete monete in sequenza e usate delle graffette: vicino a ogni moneta mettete
tante graffette quante sono le altre monete uguali presenti nella sequenza. Descrivete l’algoritmo
mediante pseudocodice e spiegate in che modo !’utilizzo di monete e graffette vi sia stato utile
nel progettarlo.
R7.29. Scrivete enunciati che, in Java, risolvano i problemi indicati, usando questo array;
int[][] values = new int[ROWS][COLUMNS];
** R7.30. Progettate mediante pseudocodice un algoritmo che assegni il valore -1 a tutti gli
elementi che si trovano nella prima e neH’ultima colonna e nella prima e ultima riga di un array
bidimensionale di numeri interi.
* R7.31. Nel Paragrafo 7.7.7 avete visto quanta attenzione bisogna porre neiraggiornamento del
valore dell’indice quando si rimuovono elementi da un vettore. Mostrate come si possa evitare
questo problema scandendo gli elementi del vettore a ritroso.
★★ R7.32.Veroofalso?
a. Tutti gli elementi di un array sono dello stesso tipo.
b. Gli array non possono avere stringhe come elementi.
c. Gli array bidimensionali hanno sempre lo stesso numero di righe e di colonne.
d. In un array bidimensionale gli elementi di colonne diverse possono avere tipi diversi.
e. Un metodo non può restituire un array bidimensionale.
f. Un metodo non può modificare la lunghezza di un array ricevuto come parametro.
g. Un metodo non può modificare il numero di colonne di un array bidimensionale ricevuto
come parametro.
* R7.35 (collaudo). Definite i termini collaudo regressivo (“regression testing’’) e pacchetto di prove
(“test suite’’).
R7.36 (collaudo). Nel collaudo, quale fenomeno viene detto ciclicità? Cosa si può fare per evi
tarlo?
Jsercizi di programmazione
E7.1. Scrivete un programma che inizializzi un array con dieci numeri interi casuali e, poi, visualizzi
quattro righe di informazioni, contenenti:
• Tutti gli elementi di indice pari.
• Tutti gli elementi di valore pari.
• Tutti gli elementi in ordine inverso.
• Soltanto il primo e l’ultimo elemento.
E7.2. Modificate il programma LargestInArray. java, visto nel Paragrafo 7.3, in modo che evidenzi
l’elemento minimo e l’elemento massimo.
408 C apitolo 7
E7.3. Scrivete il metodo sumWithoutSmallest che calcoli, usando un unico ciclo, la somma di tutti i
valori di un array, escludendo il valore minimo. Nel ciclo, aggiornate la somma e il valore minimo;
al termine, restituire la differenza tra questi due valori.
E7.4. Aggiungete alla classe Student, vista nel Paragrafo 7.4, il metodo removeMin che elimini il
punteggio minimo senza invocare altri metodi
E7.5. Scrivete un programma che legga una sequenza di numeri interi, memorizzandola in un
array, e ne calcoli la somma a elementi alterni. Per esempio, se il programma viene eseguito fornendo
questi dati
1 4 9 16 9 7 4 9 11
allora calcola
1 - 4 + 9 - 1 6 + 9 - 7 + 4 - 9+11 =- 2
E7.6. Scrivete un programma che legga una sequenza di numeri interi, memorizzandola in un
array, e ne inverta l’ordine. Per esempio, se il programma viene eseguito fornendo questi dati
I 4 9 16 9 7 4 9 11
II 9 4 7 9 16 9 4 1
E7.7. Scrivete un programma che produca 10 permutazioni casuali dei numeri da 1 a 10. Per
generare una permutazione casuale, riempite un array con i numeri da 1 a 10, facendo in modo
che non ve ne siano due uguali. Potreste farlo in modo brutale, generando numeri casuali fino a
quando non viene prodotto un valore non ancora presente nell’array, ma si tratta di una soluzione
inefficiente. Seguite, invece, questo algoritmo:
Ripeti 10 volte
Crea un secondo array e riempilo con i numeri da 1 a 10.
Scegli a caso un elemento dal secondo array.
Eliminalo dal secondo array e aggiungilo alla permutazione attuale.
E7.8. Scrivete un metodo che implementi l’algoritmo sviluppato nel Paragrafo 7.5.
E7.9. Progettate la classe DataSet che memorizzi una sequenza di valori di tipo double, con un
costruttore
public DataSet(int maximumNumberOfValues)
e un metodo
} * *
che verifichi se due sequenze contengono gli stessi valori, nello stesso ordine.
che verifichi se due sequenze contengono gli stessi valori, indipendentemente dall’ordine e
ignorando la presenza di valori duplicati. Ad esempio, le due sequenze
1 4 9 16 9 7 4 9 11
11 11 7 9 16 4 1
410 C apitolo 7
devono essere considerate uguali. Probabilmente avrete bisogno di qualche metodo ausi
liario.
che verifichi se due sequenze contengono gli stessi valori con le stesse molteplicità, indipendentemente
dall’ordine. Ad esempio, la sequenza
I 4 9 16 9 7 4 9 11
II 1 4 9 16 9 7 4 9
mentre la sequenza
I 4 9 16 9 7 4 9 11
II 11 7 9 16 4 1 4 9
che restituisca una sequenza i cui elementi siano la somma degli elementi in posizioni corrispondenti
nelle due sequenze elaborate. Ad esempio, la somma tra la sequenza
1 4 9 16 9 7 4 9 11
e la sequenza
11 11 7 9 16 4 1
è la sequenza
12 15 16 25 25 11 5 9 11
E7.15. Scrivete un programma che generi una sequenza di 20 valori casuali compresi tra 0 e 99,
memorizzandola in un array, poi visualizzi la sequenza generata, la ordini e la visualizzi di nuovo,
ordinata. Usate il metodo sort della libreria standard di Java.
E7.16. Aggiungete alla seguente classe Table un metodo che calcoli il valore medio tra gli otto
valori adiacenti nelle diverse direzioni (come visualizzato nella Figura 15 del Paragrafo 7.6.3) a
un determinato elemento della tabella:
public double neighborAverage(int row, int column)
A rray e vettori 411
Se Telemento in esame si trova in un bordo della tabella considerate soltanto gli elementi adiacenti
che appartengono effettivamente alla tabella stessa: ad esempio, se row e column valgono 0, esistono
soltanto tre elementi adiacenti.
che restituisca la somma degli elementi della riga /-esima (se il valore di horizontal è true) o della
colonna /-esima (se il valore di horizontal è false).
* * E7.18. Scrivete un programma che legga una sequenza di valori in ingresso e visualizzi un
diagramma a barre corrispondente ai valori acquisiti, simile a questo:
^t**Hi^,liHi^t,ttt*************************
^i**t**************************
Potete ipotizzare che i valori siano tutti positivi. Per prima cosa individuate il valore massimo: la
barra corrispondente a quel valore deve avere 40 asterischi e le altre devono avere un numero di
asterischi proporzionale al loro valore rispetto a tale valore massimo.
E7.19. Risolvete nuovamente l’esercizio precedente, disegnando però le barre verticalmente, con
la barra più alta costituita da venti asterischi.
k-k-k E7.20. Migliorate il programma dell’Esercizio E7.18 in modo che possa gestire correttamente
anche valori negativi.
** E7.21. Migliorate il programma dell’Esercizio E7.18 aggiungendo un’etichetta descrittiva a ciascuna
barra del diagramma, dopo aver chiesto all’utente il testo delle etichette e i valori. Il diagramma
visualizzato deve essere simile a questo:
Egypt ♦♦♦*♦♦***♦***♦*♦♦*♦**♦♦♦♦♦
France ***************♦*♦*******♦******♦*♦*♦*♦♦
Dapan ***********************************
Uruguay ******************************
Switzerland ***********
a g g iu n g e te il m e to d o
1 4 9 16
e b è la seq u en za
9 7 4 9 11
1 4 9 16 9 7 4 9 11
sen za m o d ifica re a o b.
1 4 9 16
e b è la seq u en za
9 7 4 9 11
1 9 4 7 9 4 16 9 11
sen za m o d ifica re a o b.
1 4 9 16
A rray e vettori 413
e b è la sequenza
4 7 9 9 11
1 4 4 7 99911 16
senza modificare a o b. In caso di sequenze non originariamente ordinate fondete i più lunghi
prefissi ordinati di a e b.
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
Progettare programmi che svolgono compiti complessi
• Quando si sviluppa la soluzione di un problema, è bene iniziare da un problema semplificato.
• Delineate un piano costituito da una serie di problemi, ciascuno dei quali sia un’estensione
abbastanza semplice del precedente, terminando con il problema originario.
Usare i pacchetti per organizzare insiemi di classi correlate
• Un pacchetto (package) è un insieme di classi correlate.
• La direttiva import permette di utilizzare una classe di un pacchetto usando soltanto il
nome della classe, senza il prefisso del pacchetto.
• Per costruire nomi di pacchetti non ambigui usate un nome di dominio, invertendo
l’ordine delle stringhe che lo compongono.
• Il percorso del file che contiene una classe deve corrispondere al suo nome di pacchetto.
• Variabili e metodi che non vengono dichiarati né public né private sono disponibili per
tutte le classi dello stesso pacchetto, situazione solitamente indesiderata.
Usare JUnit per scrivere collaudi di unità
• Gli ambienti per il collaudo di unità semplificano il compito di scrivere classi che contengano
molti casi di prova.
• La filosofia di JUnit prevede l’esecuzione di tutti i collaudi ogni volta che viene modificato
il codice.
loro paga oraria. Se, però, hanno lavorato più di 40 ore, le ore di straordinario vengono pagate il
150% della paga ordinaria. Fornite un nome appropriato per una classe di tipo “attore” che sarebbe
adeguata per realizzare questo programma, poi individuate il nome di una classe che non sia di
tipo “attore” ma che sarebbe una valida alternativa. In che modo la scelta tra queste due alternative
influenza la struttura del programma?
R 8 .6 . Analizzate l’interfaccia pubblica della classe jav a.lang.System e discutete la sua coesione.
Supponete che un oggetto Invoice (fa ttu ra ) contenga le descrizioni dei prodotti ordinati,
R 8 .7 .
insieme agli indirizzi di spedizione e di fatturazione del cliente. Disegnate un diagramma UML che
mostri le dipendenze tra le classi Invoice, Address ( i n d i r i z z ò ) ^ C u sto m e r (c lie n te ) e Product ( p r o d o tto ) .
Supponete che un distributore automatico contenga prodotti e che gli utenti vi inserisca
R 8 .8 .
no monete per comprarli. Disegnate un diagramma UML che mostri le dipendenze tra le classi
VendingMachine (d is tr ib u to r e a u to m a tic o ) . C oin ( m o n e ta ) e P r o d u c t ( p r o d o tto ) .
} * *
Descrivete gli effetti collaterali del metodo read. Quali sono sconsigliati, secondo quanto esposto
nel Paragrafo 8.2.4? In che modo si possono eliminare tali effetti collaterali indesiderati? Che
effetto ha sull’accoppiamento la modifica del progetto?
R8.17. Quali effetti collaterali hanno i seguenti tre metodi (se ne hanno)?
public class Coin
{
456 C apitolo 8
Idealmente un metodo non dovrebbe avere effetti collaterali. Potete scrivere un programma
R 8 .1 8 .
in cui nessun metodo ha un effetto collaterale? Sarebbe un programma utile?
R 8 .1 9 . Esaminate il metodo seguente, creato per scambiare tra loro i valori di due numeri
interi:
public static void falseSwap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args)
{
int X = 3;
int y = 4;
falseSwap(x, y);
System.out.println(x + " " + y);
}
Perché il metodo non scambia il contenuto di x e di y?
In che modo potete scrivere un metodo che scambi il valore di due numeri in virgola
R 8 .2 0 .
mobile? Suggerimento: java.awt.Point.
R 8 . 2 !.Tracciate
uno schema di ciò che accade in memoria,in modo da porre in evidenza il motivo
per cui il metodo seguente non è in grado di scambiare tra loro due oggetti di tipo BankAccount:
public static void falseSwap(BankAccount a, BankAccount b)
{
BankAccount temp = a;
a = b;
b = temp;
}
R8.22. Considerate la classe Die del Capitolo 6, modificata mediante l’aggiunta di una variabile
statica:
public class Die
{
P rogettazione di classi 457
* R8.23. Provate a compilare il programma seguente e spiegate il messaggio d’errore che si ottiene.
public class Printl3
{
public void print(int x)
{
System.out.println(x);
}
public static void main(String[] args)
{
int n = 13;
print(n);
}
}
R8.24. Analizzate i metodi della classe Integer. Quali sono statici? Perché?
R8.25. Analizzate i metodi della classe String (ignorando quelli che ricevono un parametro di
tipo char[]). Quali sono statici? Perché?
R8.26. Analizzate il problema di allineare completamente i margini di un paragrafo di testo, avendo
come obiettivo una determinata larghezza, inserendo il massimo numero possibile di parole su una
riga e distribuendo equamente spazi aggiuntivi in modo che ogni riga abbia la larghezza richiesta.
Individuate un piano di lavoro per scrivere un programma che legga un paragrafo di testo e lo
visualizzi con i margini allineati. Descrivete una sequenza di programmi intermedi di complessità
crescente, seguendo un approccio simile a quello visto nel Paragrafo 8.5.
R8.27. Le variabili in e out della classe System sono variabili statiche pubbliche. È una buona scelta
di progetto? Se non lo è, come la migliorereste?
R8.28. Qualsiasi programma Java può essere riscritto eliminando gli enunciati import. Spiegate
come ciò sia possibile e riscrivete il file RectangleComponent. java del Paragrafo 2.9.3 eliminando gli
enunciati import.
R8.29. Che cos’è il pacchetto predefinito {default package)^ L’avete usato nella vostra programma
zione prima di leggere questo capitolo?
R8.30 (collaudo). Come si comporta JUnit quando un metodo di collaudo lancia un’eccezione?
Provate e descrivete ciò che scoprite.
458 C apitolo 8
Esercìzi di programmazione
E8.1. Realizzate la classe Coin descritta nel Paragrafo 8.2 e modificate la classe CashRegister in
modo che si possano aggiungere monete al registratore di cassa mediante il nuovo metodo
void receivePayment(int coinCount, Coin coinType)
Tale metodo va invocato una volta per ogni tipo di moneta presente nel pagamento.
E8.2. Modificate il metodo giveChange della classe CashRegister in modo che restituisca il numero
di monete di un particolare tipo che vanno fornite come resto
int giveChange(Coin coinType)
Tale metodo va invocato una volta per ogni tipo di moneta, in ordine di valore decrescente.
E8.3. I veri registratori di cassa possono gestire monete e banconote. Progettate un’unica classe
che rappresenti ciò che accomuna questi due concetti, poi riprogettate la classe CashRegister e
dotatela di un metodo che consenta la registrazione di pagamenti descritti da tale classe. Uno degli
obiettivi principali è l’identificazione di un nome significativo per la classe.
E8.4. Riprogettate la classe BankAccount in modo che sia immutabile: i metodi deposit e withdraw
devono restituire nuovi oggetti BankAccount aventi il saldo opportuno.
E8.5. Riprogettate la classe Day vista nella sezione Esempi completi 2.1 in modo che sia mutabile:
i metodi addDays, nextDay e previousDay devono ora modificare il proprio parametro implicito ed
essere di tipo void. Modificate opportunamente il programma dimostrativo.
E8.6. Progettate i seguenti metodi statici per calcolare il volume e l’area della superficie di un
cubo con altezza h, di una sfera con raggio r, di un cilindro con altezza h e base circolare di raggio
r e di un cono con altezza h e base circolare di raggio r. Inseriteli nella classe Geometry e scrivete
un programma che chieda all’utente di inserire i valori per r e per h, che invochi gli otto metodi
e che visualizzi i risultati.
public static double cubeVolume(double h)
public static double cubeSurface(double h)
public static double sphereVolume(double r)
public static double sphereSurface(double r)
public static double cylinderVolume(double r, double h)
public static double cylinderSurface(double r, double h)
public static double coneVolume(double r, double h)
public static double coneSurface(double r, double h)
E8.7. Risolvete nuovamente l’esercizio precedente realizzando le classi Cube, Sphere, Cylinder e Cone.
Quale approccio è maggiormente orientato agli oggetti?
E8.8. Modificate l’applicazione vista nei Consigli pratici 7.1 in modo che gestisca più studenti:
per prima cosa chiedete all’utente di fornire i nomi di tutti gli studenti; poi, leggete i voti di tutte
le singole prove d’esame, una dopo l’altra, chiedendo il punteggio di ciascuno studente (visualiz
zandone il nome); infine, visualizzate i nomi di tutti gli studenti, con il relativo voto finale. Usate
un’unica classe, avente soltanto metodi statici.
E8.9. Risolvete nuovamente l’esercizio precedente usando più classi: progettate la classe GradeBook
(registro delle valutazioni) in modo che memorizzi un insieme di oggetti di tipo Student.
P rogettazione di classi 459
E8.10. Progettate questi due metodi che calcolano il perimetro e l’area deH’ellisse e,aggiungendoli
alla classe Geometry:
public static double perimeter(Ellipse2D.Double e)
public static double area(Ellipse2D.Double e)
La parte più difficile dell’esercizio è l’identificazione e la realizzazione di una formula corretta per
il calcolo del perimetro. Perché in questo caso è sensato usare un metodo statico?
E8.11. Progettate questi due metodi che calcolano (in gradi) l’angolo compreso tra l’asse x e la
retta che passa per due punti, e la pendenza (cioè il coefficiente angolare) di tale retta:
public static double angle(Point2D.Double p, Point2D.Double q)
public static double slope(Point2D.Double p, Point2D.Double q)
Aggiungete i metodi alla classe Geometry. Perché in questo caso è sensato usare un metodo statico?
E8.12. Progettate questi due metodi che verificano se un punto si trova all’interno o sul contorno
dell’ellisse e, aggiungendoli alla classe Geometry:
public static boolean isInside(Point2D.Double p, Ellipse2D.Double e)
public static boolean is0nBoundary(Point2D.Double p, Ellipse2D.Double e)
E8.13. Usando la classe Picture vista nella sezione Esempi completi 6.2, scrivete un metodo
public static Picture superimpose(Picture pici, Picture pic2)
che sovrapponga due immagini, generando un’immagine le cui dimensioni (larghezza e altezza)
siano uguali al valore massimo tra le dimensioni corrispondenti delle due immagini pici e
pic2. Nell’area in cui le due immagini si sovrappongono fate la media tra i colori, pixel per
pixel.
E8.14. Usando la classe Picture vista nella sezione Esempi completi 6.2, scrivete un metodo
public static Picture greenScreen(Picture pici, Picture pic2)
che sovrapponga due immagini, generando un’immagine le cui dimensioni (larghezza e altezza)
siano uguali al valore massimo tra le dimensioni corrispondenti delle due immagini pici e pic2.
Nell’area in cui le due immagini si sovrappongono usate i pixel di pici, tranne quando sono verdi,
nel qual caso usate i pixel di pic2.
* E 8 . 1 5 . Progettate il m e t o d o
che visualizzi il messaggio (prompt), legga un numero intero e verifichi se si trova tra il valore
minimo e il valore massimo specificati. In caso negativo, deve visualizzare il messaggio d’errore
(error) e ripetere l’acquisizione del dato. Inserite il metodo nella classe Input.
E 8.16. Esaminate il seguente algoritmo per calcolare x”, con n intero. Se n < 0, x” è uguale a 1/x~*\
Se n è positivo e pari, allora x" = (x”/2)2 Se n è positivo e dispari, allora x" = x”~* • x. Progettate
il metodo statico double intPower(double x, in t n) che utilizzi questo algoritmo, inserendolo nella
classe Numeric.
** Migliorate la classe Die vista nel Capitolo 6, rendendo statica la variabile generator, in modo
E 8 . 17.
che tutti i dadi condividano un unico generatore di numeri casuali.
460 C apitolo 8
Realizzate le classi CashRegister e Coin descritte nell’Esercizio E8.1, inserendole nel pac
E S . 18.
chetto money e lasciando, invece, la classe CashRegisterTester nel pacchetto predefinito.
ES. 19. Inserite la classe BankAccount in un pacchetto il cui nome sia derivato dal vostro indirizzo
di posta elettronica, come descritto nel Paragrafo 8.6, lasciando invece la classe BankAceountTester
nel pacchetto predefinito.
E S .20 (collaudo). Progettate con JUnit una classe di collaudo, StudentTest, avente tre metodi,
ciascuno dei quali collauda un diverso metodo della classe Student vista nella sezione Consigli
pratici 7.1.
E S . 21 (collaudo).Progettate con JUnit una classe di collaudo, TaxReturnTest, avente tre metodi,
ciascuno dei quali collauda una diversa situazione fiscale della classe TaxReturn vista nel Capito
lo 5.
Scrivete i metodi seguenti per disegnare le lettere H, E, L e O in una finestra
E S . 2 2 (grafica).
grafica; il parametro p indica il vertice superiore sinistro del rettangolo che racchiude la lettera.
Tracciate linee ed ellissi, ma non usate System.out né il metodo drawString. Poi, invocate ripetuta-
mente tali metodi per disegnare le parole “HELLO” e “HOLE”.
• public static void drawH(Craphics2D g2, Point2D.Double p);
• public static void drawE(Graphics2D g2, Point2D.Double p);
• public static void drawL(Graphics2D g2, Point2D.Double p);
• public static void draw0(Graphics2D g2, Point2D.Double p);
ES.24. Aggiungete alla classe BankAccount il metodo ArrayList<Double> getStatement() che restituisca
un elenco di tutti i versamenti e prelievi sotto forma di valori, rispettivamente, positivi e negativi.
Aggiungete anche il metodo void clearStatement() che svuoti l’elenco.
ES.25. Progettate la classe LoginForm che simuli la procedura di autenticazione (loditi) che caratterizza
molte pagine web, dotandola dei metodi seguenti:
public void input(String text)
public void click(String button)
public boolean IoggedIn()
Per prima cosa viene acquisita una stringa con il nome dell’utente {username), poi la parola d’accesso
(password). Il metodo click può essere invocato per simulare la pressione di un pulsante grafico
fornendo come argomento "Submit” (per inviare i dati) oppure "Reset" (per azzerare la procedura).
Dopo che l’utente si è autenticato correttamente (fornendo username, password e selezionando il
pulsante submit) il metodo IoggedIn restituisce true e ulteriori dati non hanno alcun effetto. Quando
un utente cerca di superare l’autenticazione fornendo un nome utente o una parola d’accesso non
validi, i dati acquisiti vengono ignorati, come quando si seleziona il pulsante reset. Dotate la classe
di un costruttore che riceva come parametri le stringhe username e password che devono essere
usate per l’autenticazione.
ES.26. Progettate la classe Robot che simuli un robot che si muove su un piano infinito. La po
sizione del robot è individuata da un punto sul piano (con numeri interi come coordinate) e da
una direzione di movimento (che può essere nord, est, sud o ovest). Progettate i metodi:
P rogettazione di classi 461
I m etod i turnLeft e turnRight cam biano la direzione (rispettivam ente, in senso antiorario e orario)
ma non la posizion e del robot. Il m eto d o move sposta il robot di u n ’unità nella direzione che lo
caratterizza, m entre il m etod o getDirection restituisce una stringa: "N", "E", "S" oppure "W".
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
9
Ereditarietà
F ig u r a 1
U n a g e ra rc h ia
di e re d ita r ie tà tra classi
di v e ico li I
Sedan SUV
Figura 2
U n d ia g r a m m a
di e re d ita rie tà
void processVehicle(Vehicle v)
Dato che Car è una sottoclasse di Vehicle, si può invocare tale metodo anche con un
oggetto di tipo Car:
Perché mai dovremmo progettare un metodo che elabora veicoli anziché, più
specificatamente, automobili? Questo metodo è più utile, in quanto può manipolare
qualunque tipo di veicolo (compresi autocarri e motocicli).
In questo capitolo analizzeremo una semplice gerarchia di classi che rappresentano
domande di un questionario e molto probabilmente avrete già risposto a molti questionari
valutati automaticamente da un calcolatore. Un questionario {quiz) è composto da più
domande {question), che possono essere di tipi diversi:
File Question.java
Figura 3
G e ra rc h ia d i e re d ita rie tà
p e r tip i di d o m a n d e
return response.equals(answer);
}
Questa classe Question è veramente molto elementare e non gestisce domande a risposta
multipla, domande numeriche e così via. Nei paragrafi che seguono vedrete come definire
sottoclassi di Question, intanto vediamo un semplice programma di collaudo per la classe
Question.
import java.util.Scanner;
/**
Visualizza un semplice questionario con una sola domanda.
*/
public class OuestionDemol
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
q. display 0 ;
System.out.print("Your answer: ");
String response = in.nextLine();
System.out.println(q.checkAnswer(response));
}
}
JW terM u tazio n e
1. In una relazione di ereditarietà tra le classi Employee (dipendente) e Manager (dirigente), quale
dovrebbe essere la superclasse e quale la sottoclasse?
2. Che relazioni di ereditarietà ci sono tra le classi BankAccount (conto bancario), Checking^ccount
(conto corrente bancario) e SavingsAccount (conto bancario di risparmio)?
3. Consultando la documentazione API, elencate tutte le superclass! della classe DFrame.
4. Dato il metodo doSomething(Car c), che richiede un argomento di tipo Car, elencate le
classi della Figura 1 i cui oggetti non possono essere passati come argomento a tale metodo.
5. La classe Quiz (questionario) dovrebbe essere una sottoclasse della classe Question? Perché?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R9.2, R9.8 e R9.10, al termine del
capitolo.
double milesPerCallon;
9 .2 Realizzare sottoclassi
In questo paragrafo vedrete come definire una sottoclasse e come le sottoclassi ereditino
automaticamente le funzionalità della propria superclasse.
Supponiamo di voler scrivere un programma che gestisca domande, come questa:
In which country was the inventor of Dava born?
1. Australia
2. Canada
3. Denmark
4. United States
Potreste scrivere una classe, ChoiceQuestion, partendo da zero e dotandola di metodi per
impostare il testo della domanda, per visualizzarla e per verificare se una risposta data
sia quella corretta, ma non c’è bisogno di farlo. Usate, invece, l’ereditarietà e realizzate la
classe ChoiceQuestion come sottoclasse della classe Question (come indicato nella Figura 4).
Figura 4
La classe ChoiceQuestion
è un a sottoclasse
della classe Question
InJava si definisce una sottoclasse specificando cosa la rende diversa dalla sua super-
classe.
Un oggetto di una sottoclasse possiede automaticamente tutte le variabili di esem
plare che sono dichiarate nella superclasse: occorre dichiarare soltanto quelle variabili di
esemplare che non sono presenti negli oggetti della superclasse.
La sottoclasse eredita tutti i metodi della sua superclasse e deve definire soltanto i
I sottoclasse e red ita tu tti i m eto d i
che non sovrascrive.
metodi che sono nuovi (cioè non sono presenti nella superclasse) oppure che modificano
!’implementazione di metodi ereditati, se il comportamento ereditato non è adeguato.
Quando si fornisce una nuova implementazione di un metodo ereditato si dice che lo si
Una sottoclasse può sovrascrivere
un m eto d o e re d ita to
sovrascrive (override).
d alla sua superclasse fo rn e n d o n e
Un oggetto di tipo ChoiceQuestion differisce da uno di tipo Question per tre aspetti:
una nuova im p le m en ta zio n e.
• memorizza le diverse opzioni possibili per la risposta;
• c’è un metodo per aggiungere le opzioni possibili per la risposta;
• il metodo display della classe ChoiceQuestion mostra le opzioni possibili per la risposta,
in modo che chi deve rispondere alla domanda possa sceglierne una.
Figura 5
La classe C hoiceO uestion
a g g iu n g e u n a v a ria b ile
di e s e m p la re e u n m e to d o ,
o ltre a s o v ra s c riv e rn e
un a ltro
Figura 6
S tru ttu ra in te rn a di un
o g g e tto di u n a s o tto cla s s e
Tutti gli altri metodi della classe Question vengono automaticamente ereditati dalla classe
ChoiceQuestion e possono essere invocati con oggetti della sottoclasse:
choiceQuestion.setAnswer(''2'');
choiceQuestion.addChoice("Canada", true)
Il primo argomento ricevuto deve essere aggiunto in fondo alla lista a cui fa riferimento
la variabile di esemplare choices. Se il secondo argomento è il valore true, allora la
variabile di esemplare answer deve diventare uguale al numero associato alla scelta che
si sta aggiungendo. Ad esempio, se choices.size() vale 2, allora alla variabile answer viene
assegnata la stringa "2".
Non si può accedere direttamente alla variabile di esemplare answer della superclasse,
ma, fortunatamente, la classe Question mette a disposizione il metodo setAnswer, che
possiamo invocare. Con quale oggetto lo invochiamo? Con la domanda che stiamo
modificando, cioè con il parametro implicito del metodo ChoiceQuestion.addChoice.
Ricordate: se invocate un metodo usando il parametro implicito, non c’è bisogno di
specificare esplicitamente il parametro implicito stesso, basta scrivere il nome del metodo
che si vuole invocare.
setAnswer(choicestring);
Se preferite, però, potete rendere più evidente che il metodo viene invocato con il
parametro implicito, in questo modo;
this.setAnswer(choicestring);
AutQ:.Yalutflzìonc
6. Se q è un oggetto di tipo Question e cq è un oggetto di tipo ChoiceQuestion, quali delle
seguenti invocazioni sono valide?
a. q.setAnswer(response)
b. cq.setAnswer(response)
c. q.addChoice(choice, true)
d. cq.addChoice(choice, true)
7. Data le seguente definizione della classe Employee, dichiarate la classe Manager che erediti
da Employee aggiungendo una variabile di esemplare, bonus, che memorizzi il premio di
produzione. Non indicate costruttori e metodi.
8. Quali sono le variabili di esemplare della classe Manager definita nella risposta alla domanda
precedente?
9. Nella classe Manager definita nella risposta alla domanda 7, definite l’intestazione (ma
non !’implementazione) di un metodo che sovrascriva il metodo getSalary della classe
Employee.
10. Quali sono i metodi ereditati dalla classe Manager definita nella risposta alla domanda
precedente?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R9.4, E9.9 e E9.13, al termine del
capitolo.
} * ’ *
Ora il costruttore viene certamente compilato, ma non aggiorna il testo giusto! Un tale
oggetto di tipo ChoiceOuestion ha due variabili di esemplare, entrambe di nome text. 11
costruttore assegna un valore a una delle due, mentre il metodo display (della superclasse)
visualizza !’altra. La soluzione corretta prevede di accedere alla variabile di esemplare della
superclasse attraverso l’interfaccia pubblica della superclasse stessa. Nel nostro esempio,
il costruttore di ChoiceOuestion dovrebbe invocare il metodo setText della classe Question.
• La parola riservata extends suggerisce che l’oggetto di tipo ChoiceQuestion sia una
versione “estesa” di un oggetto di tipo Question.
• L’oggetto di tipo ChoiceQuestion è “più grande”, perché ha una variabile di esemplare
in più, choices.
• L’oggetto di tipo C h o i c e Q u e s t i o n è “più potente”, perché ha un metodo in più,
addChoices.
9.3 Sovrascriverejnetodl
Una sottoclasse eredita i metodi della superclasse, ma, se il comportamento del metodo
Il m e t o d o che sovrascrive l'om onim o
La seconda parte è facile, perché le opzioni tra cui scegliere la risposta sono contenute
in una variabile di esemplare della sottoclasse stessa.
public class ChoiceOuestion extends Question
{
public void display0
{
// visualizza il testo della domanda
Ma come possiamo accedere al testo della domanda, per visualizzarlo? Non possia
mo accedere direttamente alla variabile di esemplare text della superclasse, perché
è privata.
Ereditarietà 475
Per invocare un metodo Possiamo, invece, invocare il metodo display della superclasse, usando la parola riservata
della superclasse siusa la parola super:
riservata super.
public void d isp layO
{
/ / visu a lizza i l te s to d ella domanda
super.displayO ; / / c o sì va bene
/ / visu a lizza le risp o ste tra cui sc e g lier e
}
Se dimenticassimo la parola super, il metodo non funzionerebbe in modo corretto.
public void d isp lay0
{
/ / visu a lizza i l te sto d e lla domanda
disp layO ; / / ERRORE: invoca th is.d isp la y O
/ / visu a lizza le risp o ste tra cui sc e g lier e
} ‘ ‘
Esempio
public void displayO
{
su per.disp layO ;
Invocailmetododella
superclasse invece-
del proprio metodo. Se vi dimenticate super, questo
V^metodo invoca se stesso.
476 C apitolo 9
File QuestionDemo2.java
import java.util.Scanner;
/**
Visualizza un semplice questionario con due domande a scelta singola.
*/
public class OuestionDemoZ
{
public static void main(String[] args)
{
ChoiceOuestion first = new ChoiceOuestion();
first.setText("What was the original name of the lava language?");
first.addChoice("*7", false);
first.addChoice("Duke", false) ;
first.addChoice("Oak", true);
first.addChoice("Gosling", false) ;
File ChoiceQuestion.java
import java.util.ArrayList;
/**
Una domanda con più risposte, a scelta singola.
V
public class ChoiceOuestion extends Question
{
private ArrayList<String> choices;
E reditarietà 477
Auto-valutazione
11. Cosa c’è di sbagliato nella seguente implementazione del metodo display?
12. Cosa c’è di sbagliato nella seguente implementazione del metodo display?
public class ChoiceOuestion extends Question
{
public void displayO
{
this.displayO;
for (int i = 0; i < choices.size(); i-»-+)
{
int choiceNumber = i -i- I;
System.out.println(choiceNumber + ": " + choices.get(i));
}
}
}
13. Tornate ad analizzare !’implementazione del metodo addChoice, che invoca il metodo
setAnswer della superclasse. Perché non abbiamo avuto bisogno di scrivere super.setAnswer?
14. Nella classe Manager vista nella domanda di auto-valutazione 7, sovrascrivete il metodo
getName in modo che i dirigenti abbiano un asterisco prima del nome (ad esempio, *Lin,
Sally).
15. Nella classe Manager vista nella domanda di auto-valutazione 9, sovrascrivete il metodo
getSalary in modo che restituisca la somma tra il salario e il premio di produzione.
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E9.4, E9.5 e E9.14, al termine del
capitolo.
void println(int x)
E reditarietà 479
void println(String x)
il compilatore non protesta: pensa che vogliate fornire un metodo display per gestire
argomenti di tipo PrintStream, pur ereditando l’altro metodo display privo di parametri.
Quando sovrascrivete un metodo, controllate con cura che i tipi dei parametri siano
esatti.
bisogna, invece, dire al compilatore che il metodo getSalary da invocare è quello della
superclasse.
Quando in una sottoclasse invocate un metodo della superclasse alFinterno di un
metodo avente lo stesso nome, dovete ricordarvi di usare la parola riservata super.
Nel nostro programma d’esempio abbia usato implicitamente il costruttore della superclasse
Un costruttore di una sottoclasse
pÉfornire argomenti a un costruttore
privo di parametri. Se, però, tutti i costruttori della superclasse hanno parametri, bisogna
della superclasse usando la parola
necessariamente usare la sintassi con super e fornire gli argomenti richiesti da uno di tali
riservata super. costruttori.
Quando la parola riservata super è seguita da una coppia di parentesi indica
l’invocazione del costruttore della superclasse e deve essere il primo enunciato nel
costruttore della sottoclasse. Se, invece, super è seguita da un punto e da un nome di
metodo, indica l’invocazione di un metodo della superclasse, come avete visto nel
paragrafo precedente: questo può avvenire in qualsiasi punto di qualunque metodo
di una sottoclasse.
SAPolimorfisma
In questo paragrafo vedrete come si possa usare l’ereditarietà per elaborare oggetti di tipi
diversi in uno stesso programma.
Consideriamo il primo programma usato come esempio in questo capitolo: presentava
all’utente la domanda contenuta in un oggetto di tipo Question. Il secondo programma,
invece, presentava due domande di tipo ChoiceQuestion. Siamo in grado di scrivere un
programma che presenti all’iitente un questionario con domande dei due tipi?
Ereditarietà 481
Esempio
public ChoiceOuestion(String questionText)
Per prima cosa invoca
il costruttore della
{
super(questionText); ------ - Se non c'è questa riga, viene invocato
superclasse. choices = new ArrayList<String>;
il costruttore della superclasse privo
Il corpo del costruttore può I^i parametri._____________________
contenere altri enunciati.
Con !’ereditarietà questo obiettivo è facile da raggiungere. Per presentare una domanda
all’utente non abbiamo bisogno di sapere con precisione di quale tipo sia la domanda
stessa: semplicemente, visualizziamo la domanda e verifichiamo se l’utente ha fornito
la risposta corretta. La superclasse Question mette a disposizione metodi che realizzano
entrambi questi compiti, quindi possiamo definire la seguente funzione presentQuestion,
che si aspetta di ricevere un oggetto di tipo Question:
public static void presentOuestion(Question q)
{
q.displayO;
System.out.print("Your answer: ");
Scanner in = new Scanner(System.in);
String response = in.nextLine();
System.ou t .println(q.checkAnswer(response));
}
Come detto nel Paragrafo 9.1, possiamo fornire un oggetto di una sottoclasse ogni volta
Un rife rim e n to a una sottoclasse
Figura 7
V aria b ili di tip i d iv e rs i c h e
fa n n o rife rim e n to
al m e d e s im o o g g e tt o
Figura 8
U n r ife rim e n to di tip o
Q uestion p u ò p u n ta r e
a un o g g e tto di qualsiasi
s o tto cla s s e di Q uestion
In effetti è giusto che sia così: dopo tutto, in questa invocazione del metodo presentOuestion
la variabile q fa riferimento a un oggetto ChoiceQuestion, ma in un’altra invocazione q
potrebbe fare riferimento a un semplice oggetto Question oppure a un oggetto di una
diversa sottoclasse di Question.
Ora analizziamo più in dettaglio il metodo presentQuestion, che inizia con questa
invocazione:
Quale dei due metodi display viene invocato? Se esaminate, nel seguito, ciò che viene
Q uando la m acchina v irtu a le invoca .
File QuestionDemoS.java
import java.util.Scanner;
presentQuestion(first) ;
presentQuestion(second);
}
■Aiito-Yfllutaaonfi
16. Nell’ipotesi che SavingsAccount sia una sottoclasse di BankAccount, quale dei seguenti
enunciati è valido in Java?
a. BankAccount account = new SavingsAccount();
b. SavingsAccount account2 = new BankAccount();
c. BankAccount account = null;
d. SavingsAccount account2 = account;
20. L’invocazione Math.sqrt(2) richiede una ricerca dinamica del metodo da eseguire?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R9.7, E9.7 e P9.17, al termine del
capitolo.
presentQuestion
____ _
c q .presentQuestion();
Ereditarietà 485
Quale metodo display e quale metodo checkAnswer verranno invocati all’interno del metodo
Se osservate il codice del metodo presentOuestion, noterete che tali metodi
presentOuestion?
vengono eseguiti usando il parametro implicito:
} ’ * *
Che cosa dovrebbe fare questo metodo? Potremmo naturalmente scrivere un metodo
che non fa nulla, ma un programmatore che progetta una nuova sottoclasse potrebbe
486 C apitolo 9
} * * ’
Se una classe estende una classe astratta senza fornire un’implementazione di tutti i metodi
astratti è anch’essa astratta.
Notate che non potete costruire un oggetto che sia esemplare di una classe astratta, ma
potete sempre usare un riferimento il cui tipo sia una classe astratta. Naturalmente, l’oggetto
effettivo a cui si riferisce deve essere un esemplare di una sottoclasse concreta:
Account anAccount; // corretto
anAccount = new Account(); // Errore: Account è una classe astratta
anAccount = new SavingsAccount(); // corretto
anAccount = nuli; // corretto
Ereditarietà 487
Questo significa che nessuno può estendere la classe String e, di conseguenza, una variabile
di tipo String deve contenere il riferimento a un oggetto di tipo String, non di una sua
sottoclasse (che non può esistere).
Potete dichiarare final anche singoli metodi:
In questo modo nessuno potrà sovrascrivere il metodo checkPassword con un altro metodo
che restituisca semplicemente true.
Ai dati protetti di un oggetto si può accedere dai metodi della sua classe e di tutte le
sottoclassi di questa. Per esempio, la classe ChoiceOuestion eredita da Question, quindi i suoi
metodi possono accedere alle variabili di esemplare protected della superclasse Question.
Ad alcuni programmatori piace la modalità di accesso protected, perché sembra un
compromesso fra la protezione assoluta, rendendo private tutte le variabili di esemplare,
e la totale mancanza di protezione, rendendole tutte pubbliche.Tuttavia, l’esperienza ha
dimostrato che le variabili di esemplare protette sono soggette allo stesso tipo di problemi
che affliggono quelle pubbliche. Chi progetta la superclasse non può controllare gli autori
delle sottoclassi e qualunque metodo di sottoclasse può alterare i dati della superclasse.
Inoltre, è diffìcile modificare le classi con variabili protette: se l’autore della superclasse
volesse cambiare !’implementazione dei dati, non potrebbe modificare le variabili protette,
perché qualcuno, da qualche parte, potrebbe avere scritto una sottoclasse il cui codice
dipende da esse.
Le variabili protette, in Java, hanno un altro svantaggio: sono accessibili non soltanto
dalle sottoclassi, ma anche dalle altre classi che si trovano nello stesso pacchetto (Argomenti
avanzati 8.4).
E meglio fare in modo che tutti i dati siano privati. Se volete che soltanto i metodi
di una sottoclasse abbiano accesso ai dati, valutate la possibilità di dichiarare protetto un
metodo di ispezione.
• Un conto di risparmio {savings account) che matura interessi attivi. Gli interessi
maturano mensilmente e vengono calcolati sulla base del saldo minimo registrato
durante il mese.
• Un conto corrente {checking account) che non matura interessi, consente tre prelievi
mensili senza commissione e addebita una commissione {fee) di $1 per ogni ulteriore
prelievo.
Nel caso di un versamento {deposit) o di un prelievo {withdraw) fi\ programma deve chiedere
all’utente il numero del conto e l’importo dell’operazione. Dopo ogni transazione il
programma visualizza il saldo del conto interessato.
Ereditarietà 489
Quando viene selezionata la voce “Month end” {cioè fine del mese), il programma
agisce su tutti i conti e accredita gli interessi o preleva le commissioni, in relazione al tipo
di conto su cui opera, poi visualizza il saldo di tutti i conti. L’opzione quit, ovviamente,
termina l’esecuzione del programma.
Versare denaro.
Prelevare denaro.
Ispezionare il saldo.
Eseguire l'elaborazione di fine mese.
public c la ss BankAccount
{
/**
Effettua un prelievo da questo conto.
@param amount l'im porto da prelevare
*/
public void withdraw(double amount) { . . . }
/**
Porta a termine l'elaborazione di fine mese,
se appropriata per questo conto.
*/
public void monthEndO ( . . . )
/**
Ispeziona i l saldo a ttu ale d i questo conto bancario.
@return i l saldo attu ale
*/
public double getBalance() ( . . . )
}
public c la ss BankAccount
{
private double balance;
} * ’ *
} * * *
492 C apitolo 9
} * ’ ’
Nel metodo withdraw della classe SavingsAccount viene aggiornato il saldo minimo raggiunto
durante il mese. Si noti l’invocazione dell’omonimo metodo della superclasse:
Nel metodo monthEnd della classe SavingsAccount l’interesse maturato viene accreditato sul
conto: dobbiamo invocare il metodo d e p o sit, perché non abbiamo accesso diretto alla
Ereditarietà 493
variabile di esemplare balance. Infine, il saldo minimo raggiunto nel mese viene reso
uguale al saldo attuale, per predisporre l’elaborazione per il mese successivo.
public void monthEndO
{
double in te r e st = minBalance * interestR ate / 100;
d ep osit(in te r e st);
minBalance = getBalance();
}
Il metodo withdraw della classe CheckingAccount deve ispezionare il contatore dei prelievi:
se è stato superato il limite di quelli gratuiti, viene addebitata una commissione. Anche
in questo caso il metodo deve invocare !’omonimo metodo della superclasse:
public void withdraw(double amount)
{
final in t FREE_WITHDRAWALS = 3;
final in t WITHDRAWAL_FEE = 1;
super. withdraw(amount);
withdrawals++;
i f (withdrawals > FREE_WITHDRAWALS)
{
super. withdraw(WITHDRAWAL_FEE);
}
}
i f (input.equals("D'')) { accounts[num].deposit(amount); }
e ls e { accounts[num].withdraw(amount); }
• Dipendente “a ore” {hourly em ployee), viene retribuito con una paga oraria, ma, se
lavora più di 40 ore in una settimana, le ore eccedenti vengono pagate “una volta e
mezza”.
• Dipendente salariato {salaried em ployee), viene retribuito con un salario settimanale,
indipendentemente dalle ore lavorate.
• Dirigente {m anager), è un dipendente salariato, al quale è dovuto anche un premio
di produttività {bonus).
Perognidipendente
Visualizza il nome del dipendente.
Acquisisci il numero di ore lavorate.
Calcola la paga dovuta per quelle ore.
Ispezionare il nome.
Calcolare la paga dovuta per un dato numero di ore.
Costruisce un impiegato sa la r ia to
con un dato nome e un dato sa la r io annuo.
V
public SalariedEmployee(String name, double salary) {
}
public c la s s Manager extends SalariedEmployee
{
Questi costruttori devono assegnare il nome alla propria porzione di oggetto di tipo
Employee, quindi definiamo nella classe Employee il metodo setName:
public c la s s Employee
{
public void setName(String employeeName) ( . . . )
}
Poi, naturalmente, ciascuna sottoclasse deve definire il metodo che calcola la paga
settimanale:
/ / questo metodo sovrascrive l'omonimo metodo d ella superclasse
public double weeklyPay(int hoursWorked) { . . . }
In questo caso abbiamo invocato un metodo, ma nella sezione Argomenti avanzati 9.1
abbiamo visto come si possa invocare un costruttore della superclasse. Nel costruttore
della classe Manager usiamo questa seconda tecnica:
U A K IIULU ^
La paga settimanale può, poi, essere calcolata seguendo le istruzioni presenti nella
descrizione del problema:
Nel caso della classe Manager dobbiamo invocare il metodo omonimo della superclasse
SalariedEmployee:
Nel pacchetto dei file scaricabili per questo libro, la cartella worked_example_i del Capitolo
9 contiene il codice sorgente completo delle classi.
Figura 9
La classe Object è la
superclasse di tu tte le
classi Java
Questo metodo toS trin g viene invocato automaticamente tutte le volte che concatenate
una stringa con un oggetto. Esaminate questa concatenazione:
"box=" + box;
Il compilatore può invocare il metodo to S trin g perché sa che tutti gli oggetti hanno tale
metodo, dal momento che tutte le classi estendono Object, che a sua volta definisce toString.
Come sapete, anche i numeri vengono convertiti in stringa quando li si concatena
con una stringa. Per esempio:
in t age = 18;
String S = "Harry's age i s " + age;
// S diventa "Harry's age is 18"
In questo caso il metodo toS trin g non è coinvolto, perché i numeri non sono oggetti e,
quindi, non esiste un metodo toS trin g per loro. L’insieme dei tipi fondamentali ha, però,
ben pochi elementi e il compilatore sa come convertirli in stringhe.
Proviamo a usare il metodo toS trin g con oggetti di tipo BankAccount:
Il risultato è deludente: ciò che viene stampato è il nome della classe seguito dal codice
hash (hash code) dell’oggetto, un valore che ci appare sostanzialmente casuale. Il codice
hash può essere utilizzato per distinguere oggetti diversi, perché, come vedrete in maggiore
dettaglio nella sezione Argomenti avanzati 14.1, è molto probabile che oggetti diversi
abbiano un diverso codice hash.
A noi, però, il codice hash non interessa: noi vorremmo sapere cosa c’è all'interno
Sovrascrivete il metodo t o S t r in g
in modo che restituisca una stringa
dell’oggetto, ma, naturalmente, il metodo toS trin g della classe Object non sa cosa ci sia
che descrive lo stato dell'oggetto.
all’interno della nostra classe BankAccount. Dobbiamo quindi sovrascrivere il metodo,
fonandone la nostra versione personale nella classe BankAccount. Seguiremo lo stesso
formato che usa il metodo toS trin g della classe Rectangle: prima il nome della classe, poi
i valori delle variabili di esemplare racchiusi fra parentesi quadre.
public c la ss BankAccount
{
public String toStringO
{
return "BankAccount[balance=" + balance + "]";
}
Ereditarietà 501
Quanto appena scritto è diverso dal confronto mediante l’operatore ==, che verifica,
invece, se due riferimenti puntano al medesimo oggetto:
i f (stampi == stamp2) . . .
/ / sono riferim en ti a l medesimo oggetto, come nella Figura i l
Figuralo ^
Due riferinnenti a oggetti Stamp
uguali
color = I cyan |
value = I 90 I
stamp2
V Stamp
co lo r = I cyan I
value = I 90 I
stampi = !
Due riferimenti Stamp
al medesim o oggetto
stamp2 color = I cyan |
value = I 90 I
public c la ss Stamp
{
private String color;
private in t value;
A questo punto, però, abbiamo un piccolo problema: la classe Object non sa nulla di
francobolli, quindi dichiara la variabile parametro otherObject del metodo equals in modo
che sia di tipo Object, ma quando sovrascrivete un metodo non potete modificare il tipo
dei suoi parametri. Per risolvere questo problema, eseguite un cast sulla variabile parametro,
per farla diventare di tipo Stamp:
Stamp other = (Stamp) otherObject;
Osservate che questo metodo equals può accedere alle variabili di esemplare di qualsiasi
oggetto di tipo Stamp: l’espressione o th e r .c olor è perfettamente lecita.
L'operatore in s ta n c e o f verifica
Per evitare il rischio di un cast sbagliato, si può usare l’operatore in sta n ceo f, che verifica
se un oggetto è di un determinato se un oggetto è di un determinato tipo. Ad esempio
tipo.
obj instanceof Question
restituisce true se e solo se obj può essere convertito nel tipo Question, cosa che accade se
obj fa effettivamente riferimento a un oggetto di tipo Question oppure a un oggetto di
una sua sottoclasse, come ChoiceQuestion.
Usando l’operatore in sta n ce o f si può scrivere un cast sicuro, in questo modo:
Si noti che in sta n ceo f non è un metodo, è un operatore, esattamente come + o <. Inoltre
non opera su numeri né, in generale, su valori che non siano oggetti: alla sua sinistra
deve esserci un riferimento a un oggetto, mentre alla sua destra ci deve essere il nome
di un tipo di dato.
Non usate l’operatore in sta n ceo f per aggirare il polimorfismo:
In una situazione come questa si deve realizzare un metodo nella classe Question, ad esempio
doTheTask, per poi sovrascriverlo nella classe ChoiceQuestion; l’invocazione, poi, diventa:
q.doTheTaskO;
Auto-valutazione
21. Perché l’invocazione System.out.println(System.out) visualizza un’informazione simile a
java.io.PrintStream@7a84e4?
22. Il frammento di codice seguente viene compilatoPVerrà poi eseguito? In caso di risposta
negativa, quale errore viene segnalato?
Object obj = ''Hello”;
System. o u t. p rin tln (o b j. len gth( ) ) ;
23. Il frammento di codice seguente viene compilato?Verrà poi eseguito? In caso di risposta
negativa, quale errore viene segnalato?
Object obj = "Who was the inventor of Dava?";
Question q = (Question) obj;
q.displayO;
24. Per quale motivo non memorizziamo semplicemente tutti gli oggetti in variabili di tipo
Object?
25. Se Xè un riferimento a un oggetto, che valore ha l’espressione x instanceof Object?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E9.10, E9.11 e E9.15, al termine del
capitolo.
È una strategia molto debole. Se aggiungete alla vostra gerarchia una nuova classe, come
NumericQuestion, dovrete controllare con cura tutte le parti del vostro programma che
contengono verifiche dei tipi di domande, aggiungendo un altro caso:
q.doTheTaskO;
In questo modo il metodo toS trin g visualizza correttamente il nome della classe anche
quando lo applicate a un esemplare di una sottoclasse, ad esempio SavingsAccount :
SavingsAccount momsSavings = . . .;
System.ou t. println(momsSavings);
/ / v isu a lizz a "SavingsAccount[balance=lOOOO]"
In questo modo un conto bancario di risparmio viene convertito in una stringa del tipo
e le parentesi quadre indicano quali variabili
SavingsA ccount[balance=i0000][interestR ate=5]
di esemplare appartengono alla superclasse.
Inoltre, le specifiche del linguaggio Java richiedono che, quando otherObject è n u li, il
metodo equals restituisca f a ls e .
Ecco una versione migliorata del metodo equals che prende in considerazione questi
due casi:
public boolean equals(Object otherObject)
{
i f (otherObject == n u li) { return fa lse ; }
i f (getC lassO != otherO bject.getC lassO ) { return fa ls e ; }
Stamp other = (Stamp) otherObject;
return c o lo r .eq u als(oth er.color) && value == other.value;
}
Quando sovrascrivete il metodo equals in una sottoclasse dovreste prima di tutto invocare
il metodo equals della superclasse, per verificare se le variabili di esemplare della superclasse
sono uguali. Ecco un esempio:
public CollectibleStamp extends Stamp
{
private in t year;
a. X = y;
b. y = x;
c. y =new Sandwich();
d. X= new Sub();
R9.8. Disegnate un diagramma di ereditarietà che mostri le relazioni ereditarie fra le classi
seguenti:
Person (persona)
Employee (dipendente)
Student (studente)
Instructor (docente)
Classroom (aula)
Object
R9.9. Disegnate un diagramma di ereditarietà che mostri le relazioni ereditarie fra le classi
seguenti, utilizzate in un programma orientato agli oggetti per la simulazione di traffico:
• Vehiele (veicolo)
• Car (automobile)
510 C apitolo 9
Truck (autocarro)
Sedan (automobile berlina)
Coupe (automobile sportiva)
PickupTruck (piccolo autocarro)
SportUtilityVehicle (automobile SUV)
Minivan (automobilefamiliare)
Bicycle (bicicletta)
Motorcycle (motociclo)
R9.11. In che modo un cast come (BankAccount) x differisce da un cast tra valori numerici
come (in t) x?
R9.12. Quale di queste condizioni restituisce true? Controllate la documentazione di Java
per identificare le relazioni di ereditarietà e ricordate che System.out è un oggetto di tipo
PrintStream.
Esercizi di programmazione
E9.1. Progettate la classe BasicAccount come sottoclasse di BankAccount in modo che il suo
metodo withdraw non consenta di prelevare una somma di denaro superiore a quella presente
nel conto.
E9.2. Progettate la classe BasicAccount come sottoclasse di BankAccount in modo che il suo
metodo withdraw addebiti una penale di $30 per ogni prelievo che produca uno scoperto.
E9.3. Riprogettate la classe CheckingAccount vista nella sezione Consigli pratici 9.1 in modo
che, durante un mese, il primo scoperto provochi l’addebito di una penale di $20, mentre per
ogni altro scoperto che avvenga nello stesso mese la penale sia $30.
Ereditarietà 511
E9.4. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe NumericQuestion
per gestire domande con risposta numerica. Se la differenza tra la risposta data e il valore
previsto non è maggiore di 0.01, accettate la risposta come corretta.
E9.5. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe FillInQuestion.
Un esemplare di questa classe viene costruito fornendo una stringa che contiene la risposta
corretta preceduta e seguita da un carattere di sottolineatura, come "The inventor of Dava was
_Dames Gosling_". La domanda va visualizzata così:
E9.6. Modificate il metodo checkAnswer della classe Question in modo che ignori la differenza
tra lettere maiuscole e minuscole e la presenza di spazi vuoti in numero maggiore del
previsto. Ad esempio, la risposta "DAMES gosling" deve essere ritenuta equivalente a "Dames
Gosling".
E 9.7. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe
AnyCorrectChoiceQuestion, che consenta domande con più risposte corrette, scelte tra quelle
proposte. L’esaminato deve fornire una qualsiasi delle risposte corrette. La stringa che
memorizza le risposte corrette deve contenerle tutte, separate da spazi. Aggiungete al testo
della domanda istruzioni opportune.
E9.8. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe MultiChoiceQuestion,
che consenta domande con più risposte corrette, scelte tra quelle proposte. L’esaminato deve
fornire tutte (e sole) le risposte corrette, separate da spazi. Aggiungete al testo della domanda
istruzioni opportune.
E9.9. Aggiungete il metodo addText alla classe Question e progettate una versione diversa di
ChoiceQuestion che invochi addText invece di memorizzare al proprio interno un vettore con
le risposte disponibili.
E9.10. Aggiungete il metodo toString alle classi Question e ChoiceQuestion.
E9.11. Realizzate una classe Person e due sue sottoclassi. Student e Instructor. Una persona ha
un nome e un anno di nascita, uno studente ha una disciplina di specializzazione e un docente
ha un salario. Per ogni classe scrivete la dichiarazione,! costruttori e il metodo toString. Fornite
un programma di prova per collaudare classi e metodi.
E9.12. Progettate una classe Employee: ogni dipendente ha un nome e una retribuzione.
Progettate, quindi, una classe Manager che erediti da Employee: aggiungete una variabile di
esemplare di tipo String, chiamata department, e scrivete un metodo toString che restituisca
una stringa con il nome, il reparto e la retribuzione. Progettate, infine, una classe Executive
che erediti da Manager. Fornite un metodo toString appropriato in tutte le classi e scrivete un
programma che collaudi classi e metodi.
E9.13. La classe java.awt. Rectangle della libreria standard di Java non mette a disposizione un
metodo per calcolare l’area o il perimetro di un rettangolo. Progettate la classe BetterRectangle,
sottoclasse di Rectangle, che abbia i metodi getPerimeter e getArea, senza aj^iun^ere variabili di
esemplare. Nel costruttore invocate i metodi setLocation e setSize della classe Rectangle. Scrivete
un programma che collaudi i metodi che avete progettato.
E9.14. Risolvete nuovamente l’esercizio precedente ma nel costruttore della classe
BetterRectangle invocate il costruttore della superclasse.
512 C apitolo 9
E9.15. Un “punto etichettato” {labeled point) ha una coordinata x, una coordinata y e una
stringa che funge da etichetta. Definite la classe LabeledPoint con il costruttore LabeledPoint(in t
X , int y, String lab el) e il metodo toString che visualizzi x, y e !’etichetta.
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
10 V^y-VV-'
Interfacce ■'
■•-•r}’ •' • 'r
^ :V :-*'
■*"V-*^
Ma cosa succede se il ristorante vuole avere, tra i propri clienti, anche altre creature viventi,
che non siano persone (Person), come un gatto (Cat) o un altro animale domestico? In una
situazione come questa ha senso definire un nuovo tipo di dato che abbia esattamente i
metodi che servono per soddisfare le invocazioni effettuate dal metodo di elaborazione
quando si occupa di un oggetto. Un tale tipo di dato si chiama interfaccia.
Ad esempio, il tipo interfaccia Customer, definito per rappresentare un generico cliente
di un ristorante, potrebbe avere i metodi eat (mangia) e pay (paga). 11 metodo che serve un
cliente potrebbe, quindi, essere dichiarato in questo modo:
Se le classi Person e Cat sono conformi all’interfaccia Customer, oggetti che siano esemplari
di tali classi possono essere forniti come argomenti al metodo serve.
Come esempio pratico studieremo l’interfaccia Comparable della libreria di Java: definisce
un metodo, compareTo, che, dati due oggetti, determina quale precede l’altro secondo un
determinato criterio di ordinamento. In questo modo è possibile progettare un servizio di
ordinamento che accetti raccolte di dati appartenenti a molte classi diverse: tutto ciò che
serve è che le classi siano conformi all’interfaccia Comparable. 11 servizio di ordinamento
è interessato solamente al metodo compareTo, che usa per disporre gli oggetti in ordine.
Nei paragrafi che seguono imparerete a capire quando sia utile definire un tipo
interfaccia, quali metodi deve contenere, come si definisce l’interfaccia stessa e come si
dichiarano classi che siano conformi a una data interfaccia.
Immaginiamo, ora, di voler calcolare il valore medio di oggetti di altro tipo: dobbiamo
scrivere un altro metodo. Ecco quello che elabora oggetti Country:
Ma l’accordo sul nome del metodo risolve soltanto una parte del problema, perché, in
Java, dobbiamo dichiarare anche il tipo della variabile obj. Ovviamente non possiamo
semplicemente scrivere:
Dobbiamo inventare un nuovo tipo che descriva qualsiasi classe i cui oggetti possano
essere “misurati”: nel prossimo paragrafo vedremo come farlo.
Esempio
public in terface Measurable N onvienefornitaalcuna
I metodi di uninterfaccia sono ^ ^ im p le m e n ta z io n e .
automaticamente pubblici, y ^ o u b l e getMeasureO;
J
U n’interfaccia è simile a una classe, ma ci sono parecchie differenze importanti:
A questo punto abbiamo un tipo di dato che indica la possibilità di ottenere un valore
(o una “misura”) corrispondente a un oggetto, quindi siamo in grado di realizzare un
metodo average riutilizzabile:
Questo metodo è utilizzabile con oggetti di qualsiasi classe che sia conforme all’interfaccia
M easurable.Nel prossimo paragrafo vedremo cosa deve fare una classe per rendere
“misurabili” ! propri oggetti.
Va osservato che l’interfaccia Measurable non è un tipo definito nella libreria standard:
è stato creato specificatamente per questo libro, come semplice caso di studio per la
nozione di interfaccia.
Interfacce 517
Notate che la classe deve dichiarare il metodo con accesso public, anche se per l’interfaccia
tale dichiarazione non è necessaria: tutti i metodi di un’interfaccia sono pubblici.
Dopo aver dichiarato che la classe BankAccount implementa l’interfaccia Measurable, gli
oggetti di tipo BankAccount sono anche di tipo Measurable:
Una variabile di tipo Measurable può contenere un riferimento a un oggetto che sia
esemplare di una classe qualsiasi che implementa l’interfaccia Measurable.
return area;
Il programma che conclude questo paragrafo usa un unico metodo average (definito
nella classe Data) per calcolare il valore medio di alcuni conti bancari e di alcune nazioni.
I tipi interfaccia vengono utilizzati Si tratta di un utilizzo tipico delle interfacce: definendo l’interfaccia Measurable abbiamo
per rendere il codice maggiormente reso riutilizzabile il metodo average.
riutilizzabile. La Figura 1 mostra le relazioni che esistono tra la classe Data, l’interfaccia Measurable e
le classi che la implementano. Si noti che la classe Data dipende solamente dall’interfaccia
Measurable e non è accoppiata alle classi BankAccount e Country.
Nella notazione UML le interfacce vengono contrassegnate dall’indicazione
«in terfa ee» , mentre una freccia tratteggiata con punta triangolare segnala la relazione che
esiste tra una classe e un’interfaccia da essa implementata. Occorre fare molta attenzione
alla punta delle frecce: una linea tratteggiata con la freccia a V aperta indica, invece, una
relazione di dipendenza (cioè la classe ne “usa” un’altra).
Figura 1
Diagram m a UML della
classe Dataedelleclassi
che im plem entano
l'interfaccia Measurable
File Data.java
public c la ss Data
{
Calcola la media d e lle misure d e g li o g g e tti d a ti.
@param objects un array di o g g e tti Measurable
^return la media d e lle misure
♦/
public s ta tic double average(Measurable[] ob jects)
{
double sum = 0;
for (Measurable obj : ob jects)
{
sum = sum + obj.getM easure();
}
Interfacce 519
File MeasurableTester.java
Essere “misurabile” è soltanto un aspetto di ciò che significa essere un conto bancario
O una nazione, anche se è comunque utile dichiarare questo comportamento comune,
perché consente di scrivere codice che sfrutta tale affinità, ad esempio progettando un
metodo che calcoli un valore medio.
Una classe può implementare più di un’interfaccia, ad esempio:
public c la s s Country implements Measurable, Named
.-AylflL-yalulazMìfi
1. Immaginate di voler utilizzare il metodo average per calcolare lo stipendio medio di un
array di oggetti Employee. Quale condizione deve essere soddisfatta dalla classe Employee?
2. Perché il metodo average non può avere una variabile parametro di tipo O bject[]?
3. Perché non possiamo usare il metodo average per calcolare la lunghezza media di oggetti
di tipo String?
4. Cosa c’è di sbagliato in questo frammento di codice?
Measurable meas = new Measurable();
System.ou t. println(m eas. getMeasure());
5. Cosa c’è di sbagliato in questo frammento di codice?
Measurable meas = new Country("Uruguay", 176220);
System. ou t. printIn(meas. getName() ) ;
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E l0.1, E l0.2 e E 10.3, al termine del
capitolo.
I nterfacce 521
Measurable meas;
Le interfacce non sono classi e non esistono oggetti il cui tipo sia un’interfaccia. Se una
variabile di tipo interfaccia fa riferimento a un oggetto, questo deve essere un esemplare
di una classe che implementa tale interfaccia:
Argomenti.avan^atll 0.1
Costanti nelle interfacce
Le interfacce non possono avere variabili di esemplare, ma al loro interno si possono
dichiarare costanti. Quando dichiarate una costante in un’interfaccia, potete (e dovreste)
omettere le parole riservate public s t a t i c final, perché in un’interfaccia tutte le variabili
sono automaticamente public s t a t i c final. Ad esempio:
public in terface Named
{
String NO_NAME = "(NONE)";
} ’ * *
Con questa definizione la costante Named. N0_NAME può essere utilizzata per indicare l’assenza
di un nome.
Non capita tanto spesso di dover definire costanti in un tipo interfaccia. In particolare,
bisognerebbe evitare di definire più costanti tra loro correlate (come in t NORTH = i , in t
NORTHEAST = 2, e così via) perché in tali casi è preferibile usare un tipo enumerativo, come
visto nella sezione Argomenti avanzati 5.4.
Se una classe implementa questa interfaccia e non sovrascrive il metodo getMeasure, eredita
questo comportamento predefinito.
Questo esempio specifico non è particolarmente utile: solitamente non si vuole che
tutti gli oggetti abbiano misura uguale a zero. Ecco, invece, un esempio più interessante,
dove un metodo di default invoca un altro metodo delfinterfaccia:
1. Le classi vincono. Quando una classe estende un’altra classe e implementa anche
un’interfaccia, ereditando uno stesso metodo da entrambe, la sottoclasse eredita il metodo
dalla superclasse, ignorando quello predefmito ereditato dall’interfaccia.
2. Le interfacce rimangono in conflitto. Quando una classe implementa due interfacce che hanno
uno stesso metodo predefmito, la sua sovrascrittura nella classe è obbligatoria.
public c la s s Person
{
public String name() { return firstName() + " " + lastName(); }
} * *
In questo caso il metodo definito nella superclasse vince sul metodo definito nell’interfaccia.
Se, però, Person è un’interfaccia, la situazione è diversa:
} ’ * ’
Questa classe deve sovrascrivere il metodo name, in modo che abbia un comportamento
adeguato al contesto in cui si trova: i dettagli dipendono dal motivo per cui il progettista
della classe User ha deciso di implementare l’interfaccia Named. Supponiamo, ad esempio,
che esista un metodo che verifica se in un array di tipo Named [] esistono duplicati e che
il programma lo voglia invocare per assicurarsi che i nomi degli utenti di un sistema
informatico siano unici: in tal caso, il metodo name della classe User dovrebbe restituire il
nome dell’utente.
10.2 Proarammarecon le
Nel paragrafo precedente avete visto come si possa realizzare un semplice servizio che
accetta come parametro un’interfaccia: siamo riusciti a fornire al servizio, come argomento,
oggetti di classi diverse e il servizio è stato in grado di invocare un metodo dell’interfaccia.
Nei paragrafi che seguono imparerete le regole che governano la programmazione con
interfacce in Java.
dove accounts è un array di oggetti BankAccount. Il metodo average, però, si aspetta di ricevere
un array i cui elementi siano di tipo Measurable:
public double average(Measurable[] ob jects)
Interfacce 525
Si possono effettuare conversioni La conversione dal tipo BankAccount al tipo Measurable è lecita. In generale, si può effettuare
dal tipo di una classe al tipo di una conversione dal tipo di una classe al tipo di una delle interfacce implementate dalla
un'interfaccia che sia implementata classe. Ad esempio:
dalla classe.
BankAeeount aeeount = new BankAeeount(lOOO);
Measurable meas = aeeount; / / va bene
Una variabile di tipo Measurable può anche riferirsi a un oggetto della classe Country vista
nel paragrafo precedente, dato che anch’essa implementa l’interfaccia Measurable.
Country Uruguay = new Country("Uruguay", 176220);
Measurable meas = Uruguay; / / anehe questo va bene
Però la classe Rectangle della libreria standard non implementa l’interfaccia Measurable, per
cui il seguente enunciato di assegnazione è sbagliato:
Figura 2
V a ria b ili di tip o " r ife rim e n to
a classe" e " r ife rim e n to
a in te rfa c c ia "
Ragioniamo con più attenzione sull’invocazione del metodo getMeasure. Quale metodo
getMeasure viene invocato? Le classi BankAccount e Country ne forniscono due diverse
implementazioni. Come può essere invocato il metodo giusto se !’invocante non sa
nemmeno a quale classe appartenga l’oggetto a cui fa riferimento meas?
Le invocazioni di metodi mediante Il polimorfismo entra di nuovo in azione (una prima trattazione del polimorfismo
una variabile di tipo interfaccia sono è stata fatta nel Paragrafo 9.4). La macchina virtuale Java trova il metodo corretto da
polimorfiche: il metodo da Invocare invocare individuando per prima cosa la classe di cui l’oggetto è effettivamente esemplare,
viene determinato durante per poi invocare il metodo di quella classe avente il nome in esame: se, ad esempio, meas
l'esecuzione del programma. fa riferimento a un oggetto BankAccount, allora viene invocato il metodo getMeasure della
classe BankAccount; se, invece, meas fa riferimento a un oggetto Country, allora viene invocato
il metodo getMeasure della classe Country.
526 C apitolo 10
Figura 3
U n a v a ria b ile di tip o
in te rfa c c ia p u ò fa re
r ife rim e n to a un o g g e tto
di q u alsiasi classe
c h e im p le m e n ta ta le
in te rfa c c ia
Per convertire un rife rim e n to di tipo Ora, cosa potete fare con il riferimento max? Voi sapete che si riferisce a un oggetto di
interfaccia in un rife rim e n to di tip o ' tipo Country, ma il compilatore non lo sa, per cui, ad esempio, non potete invocare il
classe serve un cast. metodo get Name:
Auto-valutazione
6. Si può usare un cast (BankAccount) meas per convertire una variabile meas di tipo Measurable
in un riferimento di tipo BankAccount?
7. Se le classi B a nk Ae eo un t e C o u n t r y implementano entrambe l’interfaccia Measurable, si può
convertire un riferimento di tipo C o u n t r y in un riferimento di tipo Ba nk Ae e o u n t ?
8. Perché è impossibile costruire un oggetto di tipo Measurable?
9. Perché si può comunque dichiarare una variabile di tipo M e a s u r a b l e ?
10. Cosa visualizza il seguente frammento di codice? Perché è un esempio di polimor
fismo?
Measurable[] data = { new BankAccount(10000) , new Country("Belgium", 30510) };
System. ou t. printIn(average(data));
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi RIO.3, RIO.4 e RIO.5, al termine
del capitolo.
Esempuarruileti IM . ...........
ù Analizzare sequenze di numeri
In questo Esempio completo analizziamo le proprietà di sequenze di numeri. Una sequenza
di numeri può essere una serie di misure, di prezzi, di valori casuali o di valori matematici
(come, ad esempio, i numeri primi). Sono molte le proprietà interessanti da analizzare.
Ad esempio, potete cercare schemi ripetitivi o regole nascoste, oppure verificare se una
sequenza è veramente casuale.
Problem a. Analizziamo la distribuzione delPultima cifra dei valori della sequenza. Per
una sequenza assegnata, vogliamo generare una tabella come questa:
105
94
81
112
89
103
103
100
108
105
La classe L astD igitD istribution analizza sequenze: usa un array di dieci contatori, aggiornati
dal suo metodo process, che riceve un riferimento di tipo Sequence e il numero di valori
da elaborare.
Si noti che il metodo non ha alcuna nozione in merito alla natura dei valori appartenenti
alla sequenza.
Per analizzare una specifica sequenza occorre progettare una classe che implementi
l’interfaccia Sequence. Ecco due esempi: la sequenza dei numeri che sono quadrati perfetti
(1 4 9 16 25 ...) e una sequenza di numeri interi casuali.
public in t next()
{
n++;
return n * n;
}
}
La classe che segue realizza l’intero processo di analisi. Notate come si evidenzi uno
schema ripetitivo (pattern) nelle ultime cifre della sequenza di quadrati perfetti.
public c la s s SequenceDemo
{
public s ta tic void main(String[] args)
{
LastD igitD istribution d i s t i = new L astD igitD istribution();
d i s t i . process(new SquareSequence(), lOOO);
d is ti.d is p la y O ;
System.ou t. p r in tln ();
105
94
81
112
89
103
103
100
108
105
Nel pacchetto dei file scaricabili per questo libro, la cartella worked_example_i del Capitolo
10 contiene il codice sorgente completo della classe.
a.compareTo(b)
Questo metodo compareTo confronta due conti bancari sulla base del loro saldo. Si osservi
che la variabile parametro del metodo compareTo è di tipo Object. Per trasformarla in un
riferimento di tipo BankAccount, usiamo un cast:
Dato che ora la classe BankAccount implementa l’interfaccia Comparable, si può ordinare un
array di conti bancari usando il metodo A rrays.sort:
.^Autervalutaziong
11. Come si può ordinare per superficie crescente un array di oggetti Country?
12. Si può usare il metodo Arrays.sort per ordinare un array di oggetti String? Controllate
la documentazione A P I della classe String.
13. Si può usare il metodo Arrays.sort per ordinare un array di oggetti Rectangle? Controllate
la documentazione A P I della classe Rectangle.
14. Scrivete un metodo, max, che trovi il maggiore tra due oggetti di tipo Comparable.
15. Scrivete un’invocazione del metodo progettato nella risposta alla domanda precedente
che calcoli il maggiore tra due conti bancari, per poi visualizzare il suo saldo.
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi ElO.lO e E 10.30,al termine del capitolo.
numero positivo se il secondo oggetto precede il primo. Avete già visto come implementare
questa decisione con due diramazioni, ma quando si confrontano numeri interi non negativi
c’è un’alternativa più semplice, basta sottrarre i due valori;
La differenza è negativa se id < other.id, è zero se i valori sono uguali ed è positiva negli
altri casi.
Questo trucco non funziona se i numeri interi possono essere negativi, perché il
calcolo della loro differenza può produrre una situazione di ovetjìow (si veda l’Esercizio
R l 0.1). Il metodo In te g e r .compare, però, funziona sempre:
I numeri in virgola mobile non si possono confrontare per sottrazione (si veda l’Esercizio
R 10.2). Si usa, invece, il metodo Double.compare;
ArflomentUvanzati 10.2
Il metodo clon e e !Interfaccia cion eab le
Sapete già che copiando il riferimento a un oggetto si ottengono semplicemente due
riferimenti al medesimo oggetto;
Che cosa potete fare se volete effettivamente creare una copia di un oggetto? Questo
è lo scopo del metodo clone; deve restituire un nuovo oggetto, avente lo stato identico a
quello di un oggetto esistente (osservate la Figura 4).
532 C apitolo I U
Figura 4 account =
C lo n a z io n e di u n o g g e tto
clonedAccount =
Il tipo del riferimento che viene restituito dal metodo clone è Object, quindi, quando
invocate il metodo, dovete usare un cast per segnalare al compilatore che il riferimento
restituito da accou n t.c lo n e () è, in effetti, di tipo BankAccount.
Figura 5
Il m e to d o O b je c t.clone
fa u n a c o p ia s u p e rfic ia le
Per la realizzazione del metodo clone nelle vostre classi. O b ject.clone è un buon punto
di partenza: tale metodo crea un nuovo oggetto dello stesso tipo dell’oggetto originario
e copia automaticamente nell’oggetto clonato le variabili di esemplare dell’oggetto
originario. Ecco un primo tentativo di realizzazione del metodo clon e per la classe
BankAccount:
public c la s s BankAccount
{
public Object clon e()
/ / incompleto
Object clonedAccount = su per.clon e();
return clonedAccount;
Questo metodo, però, va usato con cautela, perché si limita a delegare il problema della
clonazione, senza risolverlo completamente. In particolare, se un oggetto contiene un
riferimento a un altro oggetto, il metodo O b ject.clone crea una copia di tale riferimento,
non un clone dell’oggetto stesso. La figura mostra come funziona il metodo O b ject.clone
con un oggetto di tipo Customer che ha due riferimenti, uno a un oggetto di tipo String
e un altro a un oggetto di tipo BankAccount. Come potete vedere, il metodo O b ject.clone
copia i riferimenti nell’oggetto Customer clonato, anziché clonare gli oggetti a cui questi
si riferiscono: una copia di questo tipo viene detta copia superficiale {shallow copy).
C ’è un motivo che induce il metodo O b ject.clone a non clonare sistematicamente
tutti i sotto-oggetti: in alcune situazioni questo non è necessario. Ad esempio, se un
oggetto contiene un riferimento a una stringa, non vi è alcun pregiudizio nel copiare
tale riferimento, dal momento che il contenuto delle stringhe, in Java, non può mai essere
modificato. Il metodo O b ject.clone fa la cosa giusta se un oggetto contiene solo numeri,
valori booleani o stringhe; bisogna, però, usarlo con cautela nel caso in cui un oggetto
contenga riferimenti a oggetti modificabili.
Per questo motivo nel metodo O b ject.clone sono state predisposte due protezioni,per
garantire che non venga utilizzato accidentalmente. Per prima cosa, il metodo è dichiarato
protected (consultate Argomenti avanzati 9.5): questo vi impedisce di invocare x .c lo n e ()
per sbaglio, se la classe a cui appartiene x non ha sovrascritto clone come metodo pubblico.
Quale seconda precauzione,O bject.clone controlla che l’oggetto da clonare implementi
l’interfaccia C loneable;in caso contrario, lancia un’eccezione. In pratica, il metodo O bject,
clone assomiglia a questo:
public c la ss Object
{
protected Object clo n e() throws CloneNotSupportedException
{
i f (th is instanceof Cloneable)
{
/ / copia le v a r ia b ili di esemplare
}
e ls e
{
throw new CloneNotSupportedException();
}
Ripensiamo, quindi, al metodo average: misura oggetti, richiedendo che siano di tipo
Measurable. La responsabilità della misurazione ricade sugli oggetti stessi: da questo derivano
le limitazioni che abbiamo notato.
Sarebbe meglio se potessimo fornire al metodo average i dati di cui fare la media
e, separatamente, un metodo che misuri gli oggetti. In questo modo, elaborando
rettangoli potremmo fornire un metodo che calcoli l’area di un rettangolo, mentre
elaborando automobili potremmo fornire un metodo che ispezioni il prezzo di
un’automobile.
Il meccanismo di callback consente | Un metodo di questo tipo viene chiamato callback e costituisce un meccanismo per
!specificare codice che verrà eseguito | confezionare e trasferire un blocco di codice che possa essere invocato più tardi.
in un secondo momento. ! In alcuni linguaggi di programmazione esiste la possibilità di dichiarare esplicitamente
tali callback, s o t t o forma di blocchi di codice o di nomi di metodi, ma Java è un linguaggio
orientato agli oggetti, per cui dobbiamo trasformare questo meccanismo in un oggetto,
partendo dalla dichiarazione di un’interfaccia per il callback:
Il metodo average richiama semplicemente il metodo measure ogni volta che deve misurare
un oggetto qualsiasi.
Infine, implementando l’interfaccia Measurer si progetta un misuratore per specifici
oggetti. Ad esempio, ecco come misurare l’area di rettangoli. Definite la classe:
public c la ss AreaMeasurer implements Measurer
{
public double measure(Object anObject)
{
Rectangle aRectangle = (Rectangle) anObject;
double area = aRectangle.getWidth() * aRectangle.getH eight();
return area;
}
}
Notate che il metodo measure ha una variabile parametro di tipo O bject, anche se questo
particolare misuratore vuole misurare soltanto rettangoli. I tipi dei parametri del
metodo devono corrispondere a quelli dichiarati nel metodo measure dell’interfaccia
Measurer, per cui il parametro di tipo Object deve essere convertito in un R ectangle
con un cast:
Cosa potete fare con un oggetto di tipo AreaMeasurer? Vi serve per calcolare l’area di
rettangoli. Costruite un oggetto di tipo AreaMeasurer e passatelo al metodo average:
Measurer areaMes = new AreaMeasurer();
Rectangle[] rects = {
new Rectangle(5, 1 0 , 2 0 , 30),
new Rectangle(lO, 20, 30, 40)
};
double averageArea = average(r e c ts, areaMes);
F ig u r a 6
D ia g ra m m a U M L d e lla
classe Data
e d e ll'in te rfa c c ia M easurer
File Measurer.java
File AreaMeasurerJava
import java.aw t.Rectangle;
/**
Gli o g g e tti di questa c la sse misurano retta n g o li in base a lla loro area.
*/
public c la ss AreaMeasurer implements Measurer
{
public double measure(Object anObject)
{
Rectangle aRectangle = (Rectangle) anObject;
double area = aRectangle.getWidth() * aR ectangle.getH eight();
return area;
}
}
File DataJava
public c la s s Data
{
Calcola la media d e lle misure di un insieme di o g g e tti.
@param objects un array di o g g e tti
@param meas i l misuratore d e g li o g g e tti
@return i l valore medio d e lle misure
*/
538 C apitolo 10
File MeasurerTester.java
import java.aw t.R ectangle;
public c la ss MeasurerTester
{
public s ta tic void m ain(String[] args)
{
Measurer areaMeas = new AreaMeasurer();
Auto-valutazione
16. Immaginate di voler utilizzare il metodo average del Paragrafo 10.1 per trovare la
lunghezza media di oggetti String. Perché ciò non è possibile?
17. Come si può utilizzare il metodo average di questo paragrafo per trovare la lunghezza
media di oggetti String?
18. Perché il metodo measure dell’interfaccia Measurer ha un parametro in più del metodo
getMeasure dell’interfaccia Measurable?
19. Scrivete un metodo, max, con tre parametri che trovi il maggiore tra due oggetti, usando
un oggetto Measurer per confrontarli.
Interfacce 539
20. Scrivete un’invocazione del metodo progettato nella risposta alla domanda precedente
che calcoli il maggiore tra due rettangoli, per poi visualizzare la sua larghezza e la sua
altezza.
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R 10.9, E 10.8 e E 10.9, al termine del
capitolo.
Questa espressione definisce un metodo che, dato un oggetto, lo converte tramite un cast
in un riferimento di tipo BankAccount e ne restituisce il saldo.
Il termine “espressione lambda” deriva da una consuetudine della notazione mate
matica, che usa la lettera greca lambda (k) invece del simbolo -> (che, a sua volta, vuole
ricordare graficamente una freccia diretta da sinistra a destra). In altri linguaggi di pro
grammazione una tale espressione viene chiamata espressionefunzionale.
U n’espressione lambda non può stare da sola, deve essere assegnata a una variabile il
cui tipo sia un’interfaccia funzionale:
1. Viene definita una classe che implementa l’interfaccia funzionale e il suo unico metodo
astratto viene implementato usando l’espressione lambda.
2. Viene costruito un oggetto, esemplare di tale classe.
3. Alla variabile accountMeas viene assegnato un riferimento a tale oggetto.
U n’espressione lambda può anche essere fornita come argomento a un metodo: in questo
caso la variabile parametro del metodo viene inizializzata con l’oggetto costruito durante
la procedura appena delineata. Analizziamo, ad esempio, l’invocazione seguente:
540 C apitolo 10
Esattamente come nel caso precedente, viene costruito un oggetto, esemplare di una classe
che implementa l’interfaccia Measurer. Tale oggetto viene poi utilizzato per inizializzare
la variabile parametro meas del metodo average. Infatti, tale variabile parametro è definita
di tipo Measurer:
Il metodo average invoca il metodo measure con la variabile meas e questo provoca
l’esecuzione del corpo dell’espressione lambda.
Nella sua forma più semplice, un’espressione lambda contiene un elenco di parametri,
seguito dall’espressione che viene calcolata a partire da tali parametri. Se l’elaborazione
da compiere è più complessa, si può scrivere un corpo del metodo nel modo consueto,
racchiudendolo tra parentesi graffe e aggiungendo un enunciato return:
1. Chi progetta il metodo definisce un’interfaccia che descrive l’obiettivo del codice che
deve essere eseguito: un’interfaccia dotata di un unico metodo.
2. Il metodo riceve un parametro il cui tipo è quell’interfaccia e ne invoca l’unico metodo
ogni volta che ha bisogno di invocare il frammento di codice variabile.
3. Chi invoca il metodo fornisce come argomento un’espressione lambda il cui corpo è
proprio il codice che deve essere eseguito durante questa invocazione.
Vedrete ulteriori esempi di utilizzo delle espressioni lambda come gestori di eventi (nel
Paragrafo 10.5) e come comparatori (nel Paragrafo 13.8).
Il parametro di tipo, T, rappresenta il tipo degli oggetti che saranno accettati per fare
confronti dal metodo compareTo di una classe che implementa questa interfaccia e
solitamente tale tipo coincide con la classe stessa. Ad esempio, la classe BankAccount potrebbe
implementare l’interfaccia Comparable<BankAccount>, in questo modo:
L’uso del parametro di tipo comporta un vantaggio: non c’è bisogno di un cast per
convertire una variabile parametro di tipo Object nel tipo desiderato.
Analogamente, l’interfaccia Measurer può essere migliorata rendendola generica:
Il parametro di tipo specifica il tipo del parametro del metodo measure. Di nuovo, questo
rende possibile evitare il cast da Object quando si implementa l’interfaccia:
Una classe interna viene dichiarata Una classe dichiarata alFinterno di un’altra classe, come la classe AreaMeasurer di questo
dentro un'altra classe. esempio, viene detta classe interna {inner class). Questa disposizione segnalerà al lettore del
vostro programma che la classe AreaMeasurer non ha interesse al di fuori di questo metodo.
Poiché una classe interna a un metodo non è una caratteristica accessibile pubblicamente,
non c’è bisogno di documentarla in maniera estesa.
Le classi interne sono solitamente Si può anche dichiarare una classe all’interno di un’altra classe, ma al di fuori dei
utilizzate per classi con scopo metodi di quest’ultima: in questo modo la classe interna sarà visibile a tutti i metodi della
molto limitato, che non hanno classe che la contiene.
bisogno di essere visibili
in altre zone del programma. public c la s s MeasurerTester
{
c la ss AreaMeasurer implements Measurer
{
} ’ *
Quando compilate i file sorgenti di un programma che usa classi interne, guardate ai file
di classe che vengono generati nella cartella del programma: vedrete che le classi interne
vengono memorizzate in file con nomi curiosi, come M easurerTester$lAreaM easurer.class. I
nomi esatti non sono importanti: ciò che importa è che il compilatore traduce una classe
interna in un normale file di classe.
Auto-valutazìone
21. Perché si usa una classe interna invece di una classe normale?
22. Quando definireste una classe all’interno di un’altra classe ma all’esterno di tutti i metodi
di quest’ultima?
23. Quanti file di classe vengono generati compilando il programma MeasurerTester visto in
questo paragrafo?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi ElO.l 1 e E10.13,al termine del capitolo.
Interfacce 543
con il seguente:
Questo codice significa: costruisci un oggetto di una classe che implementa l’interfaccia
Measurer,definendo il metodo measure come indicato. Questo stile era piuttosto utilizzato
prima dell’introduzione delle espressioni lambda, che sono presenti in Java a partire dalla
versione 8: oggi è più semplice usare, appunto, un’espressione lambda, per cui in questo
libro non useremo classi anonime.
544 C apitolo 10
10.6 OggettiijempMca^^
!oggetto semplificato ("mock object") Quando state realizzando un programma costituito da più classi, spesso ne volete collaudare
fornisce gli stessi servizi di un altro alcune prima di averlo portato a termine: a questo scopo è molto efficace fare uso di
oggetto, ma in modo semplificato. oggetti semplificati {mock object), che forniscono gli stessi servizi previsti da un altro
oggetto, ma in modo estremamente più semplice.
Prendiamo in esame un’applicazione che gestisce un registro scolastico, con i risultati
di varie prove di più studenti. Per questo usiamo la classe GradeBook avente metodi come:
} * * *
La classe semplificata e la classe 11programma GradingProgram deve usare soltanto questa interfaccia, senza fare mai riferimento
completa implementano alla classe GradeBook; quest’ultima, ovviamente, implementa l’interfaccia !GradeBook, ma,
la medesima interfaccia. come già detto, potrebbe non essere ancora completamente definita.
Nel frattempo, prima di terminare lo sviluppo della classe GradeBook, produciamo
una sua realizzazione semplificata, facendo ipotesi drastiche: ad esempio, memorizzare i
dati in un file non è un’azione davvero necessaria per collaudare l’interfaccia utilizzata
dall’utente; inoltre, possiamo temporaneamente limitarci alla gestione di un solo studente.
}
public void save(String filename)
{
/ / non fa nulla
24. Perché è necessario che la classe semplificata e la classe effettiva implementino la medesima
interfaccia?
25. Perché !’utilizzo degli oggetti semplificati è particolarmente efficace quando le classi
GradeBook e GradingProgram sono sviluppate da due programmatori diversi?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi PIO. 19 e P 10.20, al termine del capitolo.
IO J Gestione di eventi
In questo paragrafo e nei successivi torniamo sulla programmazione grafica: vedrete
come usare i tipi interfaccia nella progettazione di interfacce grafiche per l’interazione
con l’utente.
Nelle applicazioni scritte finora i dati vengono forniti in ingresso dall’utente sotto il
controllo del programma, che chiede all’utente di inserire i dati in un ordine specifico: per
esempio, può chiedere di immettere per prima cosa un nome, seguito da un importo in
dollari. I programmi che usate quotidianamente, però, non funzionano in questo modo:
in un’applicazione dotata di una interfaccia grafica, il controllo è in mano 2lÌVutente, che
può usare tanto il mouse quanto la tastiera e può intervenire sui molti elementi grafici
dell’interfaccia, in qualsiasi ordine desideri. Ad esempio, l’utente può inserire informazioni
in campi di testo, aprire menu a discesa, premere pulsanti e trascinare barre di scorrimento,
in qualsiasi ordine, e il programma deve rispondere a tali comandi, in qualunque ordine
arrivino. Chiaramente, dover gestire molti possibili dati che giungano in ordine casuale
è molto più difficile che obbligare semplicemente l’utente a fornire i dati secondo un
ordine prestabilito.
Gli eventi dellinterfaccia utente In questi paragrafi imparerete a scrivere, in Java, programmi capaci di reagire a eventi
comprendono pressioni di tasti, generati dall’utente attraverso l’interfaccia grafica del programma, ad esempio selezionando
movimenti del mouse e pressioni una voce in un menu o premendo un pulsante del mouse. La raccolta di strumenti Java per
di suoi pulsanti, selezioni di voci la gestione delle finestre (Java windowing toolkit) prevede un meccanismo molto articolato
in menu e così via. che permette a un programma di specificare gli eventi a cui è interessato, oltre a indicare
quali oggetti devono essere avvertiti quando si verifica uno di tali eventi.
10.7.1 Ricezionedieventi
Quancio l’utente di un programma grafico digita caratteri sulla tastiera o utilizza il mouse
in un punto qualunque all’interno di una delle finestre del programma, il gestore Java
delle finestre invia una notifica al programma per segnalare che si è verificato un evento.
Il gestore delle finestre genera un numero enorme di eventi: per esempio, ogni volta che
il mouse percorre una minima distanza all’interno di una finestra, viene generato un
evento di tipo “movimento del mouse”, mentre in seguito a ogni pressione di un pulsante
del mouse vengono generati due eventi,“mouse premuto” e “mouse rilasciato”. Inoltre,
quando l’utente seleziona un pulsante grafico o una voce di un menu, vengono generati
eventi a un livello di astrazione più elevato.
La maggior parte dei programmi non vuole essere sommersa da eventi inutili. Pensate,
ad esempio, a ciò che succede quando viene selezionata con il mouse una voce di un
menu: il mouse si posiziona sulla voce di menu, poi viene premuto il pulsante del mouse,
che, infine, viene rilasciato. Piuttosto che ricevere grandi quantità di eventi del mouse non
rilevanti per la propria elaborazione, un programma può specificare che gli interessano
solamente le selezioni di voci di menu, ignorando tutti i sottostanti eventi relativi al mouse.
Se, invece, l’interazione dell’utente con il mouse serve a disegnare forme grafiche su un
canovaccio virtuale, sarà necessario tenere traccia con grande attenzione di tutti i singoli
eventi del mouse.
Un ricevitore di eventi è esemplare Ogni programma deve indicare quali eventi gradisce ricevere, installando opportuni
di una classe progettata oggetti che assumono il ruolo di ricevitori di eventi (event listener). Ci2iscun oggetto che
dal programmatore funge da ricevitore è esemplare di una classe progettata da voi e i suoi metodi contengono
deirapplicazione e i suoi metodi le istruzioni che vanno eseguite quando accadono quei particolari eventi che si intendono
descrivono le azioni da compiere ricevere.
quando si verifica un particolare Per installare un ricevitore dovete conoscere la sorgente dell’evento (event source),
tipo di evento. che è il componente dell’interfaccia grafica che genera quel particolare evento. Un
oggetto che funge da ricevitore di eventi va aggiunto alle sorgenti di evento appropriate,
Le sorgenti di eventi generano | dopodiché, quando accade l’evento che interessa, la sua sorgente invoca gli opportuni
segnalazioni relative agli eventi: | metodi di tutti i ricevitori a essa connessi.
lÉ b n e avviene uno, lo segnalano ^ Tutto ciò può certamente sembrare un po’ astruso, quindi analizzeremo in dettaglio
a tutti i ricevitori interessati. , un programma estremamente semplice che visualizza un messaggio ogni volta che viene
premuto un pulsante, come si può vedere nella Figura 7.1 ricevitori interessati agli eventi
di un pulsante grafico devono essere esemplari di una classe che implementa l’interfaccia
A ctionListener:
File ClickListener.java
import java. awt. even t. ActionEvent;
import java. awt. even t.A ctionL istener;
/*
Un r ic ev ito r e cii azion i che v isu a lizza un messaggio.
*/
public c la s s ClickListener implements ActionListener
{
public voici a c tionPerformed(ActionEvent event)
{
System.o u t.prin tln (" I was clicked");
}
}
Ogni volta che il pulsante viene premuto, il gestore Java dell’ambiente grafico invoca il
metodo
l is t e n e r . a c tionPerformed(even t) ;
La classe ButtonViewer, di cui trovate il codice nel seguito, costruisce un frame con un
pulsante, al quale associa un oggetto C lick L isten er. Potete collaudare questo programma
aprendo una finestra di console, eseguendovi ButtonViewer, premendo il pulsante grafico
con il mouse e osservando i messaggi visualizzati.
File ButtonViewer.java
import java. awt. event.ActionListener;
import javax.sw ing.!Button;
import javax.sw ing.!Frame;
} ’ ’ ’
Due sono i vantaggi principali di questa strategia. Innanzitutto, banalmente, le classi che
ricevono eventi tendono a essere molto brevi e, usando questo stile, la classe interna si
viene a trovare nel posto esatto in cui serve al suo scopo, senza creare confusione nella
restante parte del progetto. Inoltre, una classe interna ha un’interessante caratteristica: i
suoi metodi possono accedere alle variabili dichiarate nei blocchi che la contengono. Da
questo punto di vista, la dichiarazione di un metodo in una classe interna si comporta in
modo simile a quanto avviene in un blocco annidato.
!metodi di una classe interna possono Questa caratteristica è molto utile nella realizzazione di gestori di eventi, perché
accedere alle variabili dell'ambito consente alla classe interna di accedere alle variabili di cui necessita senza che ci sia bisogno
di visibilità circostante. di passarle come parametri a un costruttore della classe o a uno dei suoi metodi.
Vediamo un esempio, nel quale immaginiamo di voler accreditare gli interessi a un
conto bancario ogni volta che viene premuto un pulsante.
DButton button = new DButton("Add In terest");
final BankAccount account = new BankAccount(INITIAL_BALANCE);
Le variabili locali a cui si accede da C ’è, però, un vincolo tecnico da conoscere: nelle versioni di Java precedenti alla versione 8,
un metodo di una classe interna una classe interna poteva accedere a variabili locali dell’ambito di visibilità circostante solo
non devono essere modificate dopo se erano state dichiarate final, mentre a partire dalla versione 8 la variabile deve solamente
essere state inizializzate. essere effettivamente costante, cioè si deve comportare come una variabile final (senza essere
modificata dopo l’inizializzazione), ma non deve essere necessariamente dichiarata final.
Nel nostro esempio la variabile account si riferisce sempre al medesimo conto bancario: a
beneficio di lettori che usano Java nella versione 7 o precedente, !’abbiamo dichiarata final.
Una classe interna può anche accedere alle variabili di esemplare della classe circostante,
nuovamente con una restrizione: la variabile di esemplare deve appartenere all’oggetto che
ha costruito l’esemplare di classe interna. Se l’esemplare di classe interna è stato costruito
all’interno di un metodo statico, può accedere alle sole variabili statiche circostanti.
Ecco, infine, il codice sorgente del programma.
/**
Questo programma illu s t r a i l funzionamento di una c la sse
interna che accede a una varia b ile di un blocco circo sta n te.
*/
public c la ss InvestmentViewerl
{
private s ta tic final in t FRAME_WIDTH = 120;
private s t a t ic final in t FRAME_HEICHT = 60;
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R 10.16, R 10.22 e E l0.17, al termine
del capitolo.
}
}
Per quanto riguarda il compilatore, a questa classe manca la definizione del metodo
public void actionPerformed(ActionEvent event)
Dovete leggere con cura il messaggio d’errore, facendo attenzione ai tipi dei parametri:
scoprirete così il vostro errore.
Non si dovrebbe mai invocare un metodo di un ricevitore di eventi: tale metodo viene
invocato dal gestore Java delle finestre quando l’utente del programma ha fatto in modo
che l’evento accada (ad esempio, premendo il pulsante del mouse).
button. addAetionListener(
(AetionEvent event) -> System.o u t.p r in tIn("I was e lie k e d ." ));
F ig u r a 8 F
U n 'a p p lic a z io n e d o ta ta
d i p u ls a n te g ra fic o
Per prima cosa costruiamo un oggetto di tipo DButton, fornendo al costruttore l’etichetta
del pulsante:
DButton button = new DButton("Add In terest");
Usate un contenitore di tipo La finestra principale della nostra applicazione contiene sia il pulsante sia l’etichetta, ma
DPanel per raggruppare non possiamo aggiungere banalmente i due componenti direttamente al fraine, perché
insieme più componenti verrebbero posizionati uno sopra l’altro, coprendosi. La soluzione consiste nell’utilizzo
deirinterfacciajrafica. di un pannello, cioè un contenitore per componenti dell’interfaccia utente, per poi
Interfacce 553
!fazioni conseguenti alla pressione A questo punto siamo pronti per la parte difficile: il ricevitore di eventi che gestisce le
di un pulsante grafico vanno pressioni del pulsante. Come nel paragrafo precedente, è necessario definire una classe
specificate mediante classi che implementi l’interfaccia A ctionL istener, inserendo l’azione desiderata all’interno del
che implementano l'interfaccia suo metodo actionPerformed. La nostra classe di gestione degli eventi accredita gli interessi
A c t io n L is t e n e r . sul conto e ne visualizza il saldo aggiornato:
c la ss AddInterestListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
double in te r e st = account.getBalance() * INTEREST_RATE / 100;
account. d ep osit( in te r e s t) ;
label.setT ext("balance = " + account.getB alance());
}
}
File lnvestmentViewer2.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DButton;
import javax.swing.DFrame;
import javax.swing.DLabel;
import javax.swing.!Panel;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setDefaultCloseOperation(DFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Auto-valutazione
31. C o m e si fa a v isu alizzare il m essag g io "balance: . . . " alla sinistra del p u lsa n te "Add
Interest"?
32. Perché n o n è necessario dichiarare final anche la variabile button?
Perfarpratica
A q u e s to p u n to si co n sig lia di sv o lg ere gli esercizi E 1 0 .1 8 , E 1 0 .1 9 e E l 0 .2 0 , al te rm in e
d el c a p ito lo .
}
}
A p a r tire d a q u e s to m o m e n to , il te m p o r iz z a to r e in v o c a il m e to d o actionPerform ed
d e ll’o g g e tto lis te n e r a in te rv a lli re g o la ri, c h e d u ra n o u n n u m e ro di m illise c o n d i u g u a le
a in te rv a l.
I nterfacce 557
File RectangleComponent.java
import java.avrt.Graphics;
import java.awt.Graphics2D;
import jav.awt.Rectangle;
import javax.swing.DComponent;
/*
Componente che visualizza un rettangolo che può essere spostato.
public RectangleComponentO
{
// il rettangolo che viene disegnato dal metodo paintComponent
box = new Rectangle(BOX_X, BOX_Y, BOX_WIDTH, BOX_HEICHT);
}
/**
Sposta il rettangolo della quantità specificata.
@param dx l'entità dello spostamento nella direzione x
@param dy l'entità dello spostamento nella direzione y
*/
public void moveRectangleBy(int dx, int dy)
{
box.translate(dx, dy);
repaintO;
}
}
File RectangleFrame.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DFrame;
import javax.swing.Timer;
public RectangleFrameO
{
scene = new RectangleComponent();
add(scene);
setSize(FRAME_WIDTH, FRAME_HEIGHT);
File RectangleViewer.java
import javax.swing.!Frame;
/**
Questo programma visualizza un rettangolo in movimento.
*/
I nterfacce 559
Perfarpratica
A q u e sto p u n to si co n sig lia di sv o lg ere gli esercizi E 10.27 e E 1 0 .28, al te rm in e d el cap ito lo .
10.10 Eventidelmouse
Per catturare gli eventi del mouse Se sc riv e te u n p ro g ra m m a c h e m o stra d e i d ise g n i e v o le te c h e l’u te n te possa m a n ip o la rli
H H u n ricevitore di eventi del mouse u sa n d o il m o u se , d o v e te e la b o ra re e v e n ti d el m o u s e p iù co m p lessi d e lle se m p lic i p ressio n i
{mouse listener). di p u lsa n ti o d eg li ev e n ti p e r io d ic i g e n e ra ti da u n te m p o riz z a to re .
U n ric e v ito re di e v e n ti del m o u s e d ev e realizzare l’in te rfa c c ia MouseListener, c h e
c o n tie n e q u e sti c in q u e m e to d i:
File RectdngleComponent2.jdva
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.!Component;
/**
Componente che visualizza un rettangolo che può essere spostato.
*/
public class RectangleComponent2 extends !Component
{
private static final int BOX_X = 100;
private static final int B0X_Y = 100;
private static final int BOX WIDTH = 20;
I nterfacce 561
public RectangleComponent2()
{
// il rettangolo che viene disegnato dal metodo paintComponent
box = new Rectangle(BOX_X, B0X_Y, B0X_WIDTH, BOX_HEICHT);
}
Figura 9
Un "click" del mouse sposta
il rettangolo
File RectangleFrame2.java
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import javax.swing.!Frame;
/**
Questo frame contiene un rettangolo che si sposta.
*/
public class RectangleFrameZ extends !Frame
{
private static final int FRAME_WIDTH = 300;
private static final int FRAME_HEIGHT = 400;
public RectangleFrame2()
{
scene = new RectangleCofnponent2();
add(scene);
setSize(FRAME_WIDTH, FRAME_HEIGHT);
File RectangleViewer2.java
import javax.swing.!Frame;
Auto-valutazione
35. P erch é nella classe RectangleComponent2 il m e to d o moveRectangleBy è stato so stitu ito dal
m e to d o moveRectangleTo?
36. P erché la classe MousePressListener deve avere cin q u e m eto d i?
} ’ ' *
scene.setFocusable(true);
scene.requestFocus();
} * * *
I nterfacce 565
}
}
ComputerjtM dfitàlOJ...
JLAA
Codice aperto (open source) d is trib u iti c o n lic e n z e di tip o “ o p e n u n fa m o so sc ie n z ia to in fo rm a tic o e
e software libero s o u rc e ” (codice aperto, c io è n o n se g re v in c ito re del p re m io M a c A rth u r “ g e
L a m a g g i o r p a r t e d e l le a z i e n d e to ) c h e c o n c e d o n o agli u tiliz z a to ri n iu s ” , d e sc riv e il “ m o v im e n to p e r il
p r o d u ttr ic i di s o ftw a re c o n s id e r a n o il d ir itt o d i is p e z io n a re , m o d ific a re so ftw are lib e ro ” c h e so stien e il d iritto
il c o d ic e s o r g e n te alla s tre g u a d i u n e r id is tr ib u ire il c o d ic e s o rg e n te di degli U tih zzato ri del softw are di p o te r
s e g re to in d u s tria le . D o p o tu tto , se i u n p r o g ra m m a . c o n tro lla re il su o c o m p o r ta m e n to .
c lie n ti O i c o n c o r r e n ti av essero a c L’a c c e s s o al c o d i c e s o r g e n te Si tra tta di u n a p o siz io n e etica c h e
cesso al c o d ic e s o rg e n te , p o tr e b b e ro n o n è su ffic ie n te p e r g a ra n tire c h e va al di là d elT u tiliz z o di so ftw are a
s tu d ia r lo e p r o g e tta r e p r o g r a m m i il so ftw a re p o ssa so d d isfa re le esi c o d ic e a p e rto p e r i soli m o tiv i legati
sim ili sen za r ic o m p e n s a r e e c o n o g e n z e d e i su o i u tiliz z a to ri. A lc u n e al risp a rm io .
m ic a m e n te il v e n d ito r e o r ig in a rio . a z ie n d e h a n n o c re a to so ftw are c h e S tallm an h a fo n d a to il p ro g e tto
A i c lie n ti n o n p ia c e il c o d ic e so r sp ia i p r o p r i u te n ti o p p u r e lim ita GNU (http://gnu.o rg /g nu /th e- gn u-
java.awt.Component mouseClicked
addKeyListener mouseEntered
addMouseListener mouseExited
repaint mousePressed
setFocusable mouseReleased
java.awt.Container ja v a .Iang. Comparable<T>
add compareTo
java.awt.Dimension java.lang.Double
java.awt.Rectangle compare
setLocation java.lang.Integer
ja v a .a w t.event.A ctionListener compare
aetionPerformed javax.swing.AbstractButton
java.awt.event.KeyEvent addActionListener
ja v a .a w t.event.Key L is ten e i javax.swing.1Button
keyPressed javax swing.!Label
keyReleased javax.swing.!Panel
keyTyped javax.swing.Keystroke
java.awt.event.MouseEvent getKeyStrokeForEvent
getX javax.swing.Timer
getY start
ja v a .a w t.event.MouseListener stop
R I O .2 . La variab ile a di tip o double c o n tie n e il valore 0 .6 e la variab ile b, a n c h ’essa di tip o double,
c o n tie n e il valore 0 .3 . Q u a l è il risu ltato di (int)(a - b)? E di (int)(b - a)? Q u a l è il risultato di
Double.compare(a, b)? E di Double.compare(b, a)?
Cc =
I i =
3 ! =
a. C = i;
b. ! = Cj
c. i = !J
R I O .4 . S u p p o n e te c h e C sia un a classe c h e im p lem e n ta le in terfa cce I e ! e c h e i sia dichiarata e
in izializzata in q u esto m o d o :
I i= new C();
Q u a li d ei se g u e n ti a ssegn am en ti lancia u n ’e c c e z io n e ?
a. C c = (C) i;
b. ! j = (!) i;
c. i = ( I ) n u li;
I nterfacce 569
Q u a li d ei se g u e n ti a ssegn am en ti so n o leciti?
a. e = sub;
b. sub = e;
c. sub = (Sandwich) e;
d. sub = (Sandwich) cerealBox;
e. e = cerealBox;
f. e = (Edible) cerealBox;
g. e = (Rectangle) cerealBox;
h. e = (Rectangle) null;
a. Rectangle a = r;
b. Shape b = r;
c. String C = r;
d. ActionListener d = r;
e. Measurable e = r;
f Serializable f = r;
g. Object g = r;
Rectangle getBounds()
Shape S = . . .;
Rectangle r = s.getBounds();
R I O .1 3 . C o n sid era te q u este classi, una di p r im o liv ello e un a in tern a. A quali variabili p u ò a cced ere
il m e to d o f ?
public class T
{
private int t;
class C implements I
{
public void f()
{
final int c;
k-k-k R I O .15 ( g r a f i c a ) . C o m e rio rgan izzereste il p rogram m a InvestmentViewerl se d o v este ren dere
AddInterestListener u n a classe di p r im o liv e llo (c io è una classe n o n intern a)?
Esercizi di proarammaaone
E l O . l . A g g iu n g e te alla classe Data u n m e to d o c h e restituisca l ’o g g e tto aven te la m isura m a g g io re,
c o n questa firm a:
Il p r im o m e to d o d ev e restituire la stessa seq u en za p rod otta dalla classe SquareSequence vista in qu ella
s e z io n e , m e n tre il se c o n d o ha u n c o m p o r ta m e n to an alogo, m a la seq u en za p rod otta c o n tie n e i
m u ltip li a n z ich é le p o te n z e .
E10.27 (grafica). S criv ete u n p rogram m a c h e usi u n te m p o riz za to r e p er visualizzare l ’ora esatta
u n a volta o g n i se c o n d o . S u g g e r im e n to : il c o d ic e s e g u e n te visualizza l ’ora esatta e la classe Date si
trova n el p a c c h e tto java.util:
Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
11
Ingresso/usdta
e gestione delle eccezioni
, ri
while (in.hasNextDoubleO)
{
double value = in.nextDouble();
. . . // elabora value
}
Per scrivere file di testo usate P e r sc riv e re d ati in u n file si c o s tru isc e u n o g g e tto di tip o PrintWriter, fo rn e n d o il
la classe P r in t W r it e r n o m e d el file, c o m e in q u e s to e se m p io :
e i suoi metodi
PrintWriter out = new PrintWriter("output.txt");
p r in t /p r in t ln / p r in t f .
out.printIn("Hello, World!");
out.printf("Total: %8.2f\n", total);
in.closeO;
out.closeO;
I ngresso/ uscita E gestione delle eccezioni 577
S e il v o s t r o p r o g r a m m a t e r m i n a !’e s e c u z i o n e s e n z a a v e r c h i u s o u n o g g e t t o d i t i p o
P rin tW riter, p u ò d a rsi c h e n o n t u t t i i d a ti s ia n o sta ti r e a lm e n t e s c r it t i n e l f i le su l d is c o .
11p r o g r a m m a s e g u e n t e m e t t e a ll’o p e r a q u e s ti c o n c e t t i : l e g g e u n f ile d i t e s t o c o n t e n e n t e
n u m e r i e li s c r iv e in u n a ltr o f ile , i n c o l o n n a t i e s e g u i t i d a lla lo r o s o m m a .
S e , a d e s e m p i o , il f ile d ’in g r e s s o c o n t i e n e q u e s t i d ati:
32 54 67.5 29 35 80
115 44.5 100 65
a llo r a il p r o g r a m m a p r o d u c e il s e g u e n t e file :
32.00
54.00
67.50
29.00
35.00
80.00
115.00
44.50
100.00
65.00
Total: 6 2 2 .0 0
C ’è , p e r ò , u n u lt e r i o r e p r o b le m a c o n c u i c o n f r o n t a r s i. S e il f ile d a c u i l o Scanner d e v e
l e g g e r e n o n e s is te , n e l m o m e n t o in c u i lo Scanner v i e n e c o s t r u i t o si v e r if ic a l ’e c c e z i o n e
FileNotFoundException: il c o m p i la t o r e e s i g e c h e g li d i c i a m o e s p l i c i t a m e n t e c o m e v o g l ia m o
c h e il n o s t r o p r o g r a m m a r e a g is c a in u n a ta le s i t u a z i o n e . A n a lo g a m e n t e , il c o s t r u t t o r e
d i PrintWriter g e n e r a q u e s ta e c c e z i o n e se n o n è in g r a d o d i s c r iv e r e n e l f ile (c o s a c h e
p u ò s u c c e d e r e q u a n d o il n o m e d e l f i le n o n è v a l id o o p p u r e l ’u t e n t e n o n p o s s i e d e
le a u t o r i z z a z i o n i n e c e s s a r ie p e r c r e a r e u n f ile n e lla p o s i z i o n e s p e c if ic a t a ) . N e l n o s t r o
s e m p l i c e p r o g r a m m a , se si v e r if ic a ta le e c c e z i o n e v o g l i a m o p o r r e t e r m i n e a ll’e s e c u z i o n e
d e l m e t o d o main. P e r q u e s t o è s u f f ic i e n t e a g g i u n g e r e al m e t o d o main u n a d i c h i a r a z i o n e
throws, in q u e s t o m o d o :
N e l p a r a g r a fo 1 1 .4 v e d r e m o c o m e g e s tir e in m o d o p iù p r o f e s s io n a le le e c c e z i o n i .
L e c la ssi File, PrintWriter e FileNotFoundException s o n o c o n t e n u t e n e l p a c c h e t t o java.
FileTotal.jdva
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Scanner;
double total = 0;
while (in.hasNextDoubleO)
{
double value = in.nextDouble();
out.printf("%15.2f\n", value);
total = total + value;
}
in.closeO;
out.closeO;
Auto-valutazìone
1 . C o s a s u c c e d e se al p r o g r a m m a Total v i e n e f o r n it o lo stesso n o m e p e r i file d i in g r e s so e
d i u scita? S e n o n s ie te s ic u r i d e lla r isp o sta , p rovate.
2 . C o s a s u c c e d e se al p r o g r a m m a Total v ie n e fo r n it o c o m e n o m e d e l file d ’in g r e s so il n o m e
d i u n file in e siste n te ?
3 . I m m a g in a te di v o le r a g g iu n g e r e il to ta le alla fin e d e l file g ià e s is te n te , in v e c e d i sc r iv e r n e
u n o n u o v o . La d o m a n d a di a u to -v a lu ta z io n e n u m e r o 1 d ic e c h e n o n è p o s sib ile sp ec ific a re
s e m p l i c e m e n t e lo ste ss o n o m e p e r i file d i in g r e s s o e d i u s c ita . C o m e si p u ò r is o lv e r e
q u e s to p r o b le m a ? D e lin e a t e u n a s o lu z i o n e m e d ia n t e p s e u d o c o d ic e .
4. C o m e m o d if ic h e r e s t e il p r o g r a m m a Total p e r fare in m o d o c h e scriv a la m e d ia d e i v a lo ri
a c q u isiti, in v e c e d e lla lo r o so m m a ?
5. C o m e m o d if ic h e r e s t e il p r o g r a m m a Total p e r fare in m o d o c h e sc r iv a i v a lo r i su d u e
c o lo n n e , in q u e s to m o d o ?
32.00 54.00
67.50 29.00
35.00 80.00
115.00 44.50
100.00 65.00
Total: 622.00
I ngresso/ uscita E gestione delle eccezioni 579
D a q u e s to p u n to in p o i p o te te le g g e re il c o n te n u to della p a g in a w e b u sa n d o lo Scanner n el
m o d o c o n s u e to . 11 c o s tru tto re di URL e il m e to d o openStream p o sso n o la n cia re u n ’e c c e z io n e
di tip o lOException, q u in d i d o v e te a g g iu n g e re al m e to d o main la clau so la throws IOException
(n el P arag rafo 1 1 .4 .3 tro v e re te m a g g io ri in fo rm a z io n i sulla clau so la throws).
La classe URL si tro v a n el p a c c h e tto java.net.
U n a fin e s tr a d i d ia lo g o n Open
d i t ip o D F ile C h o o s e r Si invoca
il m etodo
L o o k jn : P i api [.ì CS e C3 gg:|g=j
showOpenDialog .............-............ .....I -J
File Name:
Files o f Iv p e :
} * * *
e u n PrintWriter:
582 C apitolo 11
Tl .2 Acquisite e scriverelesti
In q u e s to p a ra g ra fo im p a re re te a e la b o ra re d ati di tip o te stu a le c o n il livello di c o m p lessità
tip ic o d ei p ro b le m i reali.
11.2.1 Acquisireparole
Il metodo n e x t legge una stringa Il m e to d o next d ella classe Scanner acq u isisce la strin g a successiva. C o n s id e ra te q u e s to ciclo:
delimitata da caratteri di spaziatura.
while (in.hasNextO)
{
String input = in.next();
System.ou t.println(input);
}
F o rn e n d o in in g resso il te sto :
Mary
had
a
little
lamb
snow.
1729
C-H-
Ingresso/ uscita E gestione delle eccezioni 583
while (in.hasNextO)
{
char eh = in.next().charAt(o);
. . . // elabora eh
}
Character.isDigit(eh)
re stitu isc e tru e se eh è u n a cifra c o m p re s a tra '0 ' e '9 ' o p p u r e u n a cifra in u n a ltro
siste m a di s c rittu r a , c o m e d e s c r itto n e lla s e z io n e C o m p u t e r e so c ie tà 4 .2 , a ltr im e n ti
re stitu isc e false.
Tabella 1
Metodo Esempi di caratteri
M etodi per classificare
caratteri isDigit 0,1,2
isLetter A, B, C, a, b, c
isUpperCase A,B,C
isLowerCase a, b, C
isWhiteSpace caratteri di spaziatura
11.2.4 Acquisirerighe
Il metodo n e x t L in e legge Q u a n d o o g n i rig a di u n file di te sto c o stitu isc e u n d a to u n ita r io (d e tto record), spesso è
unintera riga. m e g lio le g g e re l’in te ra rig a c o n il m e to d o nextLine:
China 1330044605
India 1147995898
United States 303824646
D al m o m e n to c h e a lc u n e n a z io n i h a n n o n o m i c o m p o s ti da p iù p aro le , sa re b b e s c o m o d o
le g g e re q u e s to file u sa n d o il m e to d o next: d o p o aver le tto , ad ese m p io , la p aro la United,
c o m e fa re b b e il p ro g ra m m a a sa p ere di d o v e r le g g e re a n c o ra u n a p a ro la , p rim a di le g g e re
il v alo re c h e e s p rim e la p o p o la z io n e ?
L e g g ia m o , in v ece, c iasc u n a rig a in te ra , m e m o riz z a n d o la in u n a strin g a :
while (in.hasNextLineO)
{
String line = in.nextLine();
. . . // elaborazione della stringa line
}
T ro v ia m o la p rim a cifra:
int i = 0;
while ( !Character.isDigit(line.charAt(i))) { i++; }
countryName = countryName.trim();
Il m e to d o trim re stitu isc e u n a c o p ia d ella strin g a o rig in a ria , e lim in a n d o tu tti i c a ra tte ri
di sp a zia tu ra iniziali e finali.
?rr^n?|Sj l3|oÌ3l8|2|4|6rTTC]
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
__________________A ________________________ J
Y Y
countryName population
A b b ia m o a n c o ra u n p ro b le m a : la p o p o la z io n e è m e m o riz z a ta in u n a strin g a , n o n in u n a
v ariab ile n u m e ric a .V e d re m o n el P arag rafo 1 1 .2 .6 c o m e c o n v e rtire la strin g a in u n n u m e ro .
if (in.hasNextIntO)
{
int value = in.nextlnt();
China
1330044605
India
1147995898
United States
303824646
e di le g g e rlo c o n q u e s to fra m m e n to di c o d ic e :
while (in.hasNextLineO)
{
String countryName = in.nextLine();
int population = in.nextlnt();
. . . // elaborazione di countryName e population
}
In iz ia lm e n te in in g resso a b b ia m o :
C I h i i l n T a T \n ] l ] 3 1 4 1 0 1 0 1 4 1 4 1 6 ] 0 ] 5 |\ n | 1 1 n | d i i | a | w
I l I 3ì 4T oT^4R ”llT5T5T^nTn'in \n
MI] \n
Cookies: 3.20
Cookies: 3.20
Linguine: 2.95
Clams: 17.29
In q u e s to e s e m p io v e d ia m o d u e d iv e rse sp e c ific h e di fo rm a to :
^Due cifre d o p ^
il punto
decim ale
In g r e s s o / u s c it a E g e s t io n e d e l l e e c c e z io n i 589
• Il p r im o c a ra tte re è %.
• P o i, tro v ia m o d e lle s e g n a la z io n i fa c o lta tiv e (d e tte flag) c h e m o d if ic a n o il f o r m a to
stesso, c o m e a v v ien e , ad e se m p io , c o n il flag -, c h e in d ic a P in c o lo n n a m e n to a sinistra.
La T ab ella 2 ria ssu m e i flag p iù u tilizz ati.
• A s e g u ire tr o v ia m o !’a m p ie z z a d e l c a m p o , c io è il n u m e r o to ta le d i c a r a tte r i c h e
c o stitu isc e il c a m p o v isu a liz za to (c o m p re si gli spazi in se riti p e r il padding), seguìt2i da
u n a p re c isio n e (facoltativa) p e r i n u m e ri in v irg o la m o b ile .
• La s p e c ific a d i f o r m a t o t e r m i n a c o n il tipo di formato, c o m e f p e r i n u m e r i in
v irg o la m o b ile e s p e r le s trin g h e . Q u e s ti tip i n o n s o n o m o lti e Ia T a b e lla 3 m o s tra
i p rin c ip a li.
Tabella 2
Flag Significato E s e m p io
Flag di form ato
- In co lo n n a a sinistra 1.23 se g u ito da spazi
0 M ostra zeri a sinistra 001.23
+ M ostra il se g n o + n ei n u m eri positivi +1.23
( R a c c h iu d e tra parentesi to n d e i n u m er i n egativi (1.23)
} M ostra il separatore d elle m igliaia 12,300
A
U sa lettere m a iu sco le 1 .23E+1
Tabella 3
C o d ic e T ip o E s e m p io
Tipi di form ato
d N u m e r o in tero in base d ecim a le 123
f N u m e r o in virgola fissa 12.30
e N u m e r o in n o ta z io n e e sp o n en zia le 1.23E+1
g N u m e r o g e n e r ic o (v ien e usata la n o ta z io n e 12.3
e sp o n en zia le per valori m o lto grandi
O m o lto p icco li)
S Stringa Tax:
double Xl = in.nextDouble();
double x2 = in.nextDoubleO;
9. Se u n file co n tie n e una sequenza di n u m e ri, alcuni dei quali sono, p erò , assenti e v en g o n o
sostituiti dalla stringa N/A (Not Available, cioè “ n o n d isp o n ib ile”), co m e si possono acquisire
i n u m e ri e ig n o rare i segnaposto dei valori m ancanti?
10. C o m e si p o sso n o e lim in a re gli spazi dal n o m e della n a z io n e , n e ll’e se m p io v isto n el
Paragrafo 11.2.4, senza usare il m e to d o trim?
Perfarpratica
A q u e s to p u n to si co n sig lia di sv o lg ere gli esercizi E l 1.4, E l 1.6 e E l 1.7, al te r m in e d el
c a p ito lo .
Argomenti a v a n z a tili
Acquisire un intero file
N e l p a ra g ra fo p re c e d e n te av ete v isto c o m e a c q u isire rig h e , p a ro le e c a ra tte ri da u n file. In
a lte rn a tiv a , è p o ssib ile le g g e re l ’in te ro file e m e m o riz z a rlo in u n v e tto re di rig h e (lines)
O in u n ’u n ic a strin g a (content), u sa n d o le classi Files e Paths d el p a c c h e tto java.nio.file,
in q u e s to m o d o :
String filename = . . .;
ArrayList<String> lines = Files.readAllLines(Paths.get(filename));
String content = new String(Files.readAllBytes(Paths.get(filename)));
Figura 1 normale
e e t m e a t t h e
Cifratura di Cesare
P h h W -B. h d W W k h
Il p ro g ra m m a r ic h ie d e i se g u e n ti a rg o m e n ti sulla rig a d i c o m a n d o :
A d ese m p io :
File CaesarCipher.jdva
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Scanner;
In g r e s s o / u s c i t a E g e s t io n e d e l l e e c c e z io n i 593
5
6 /**
7 Questo programma cifra un file usando il cifrario di Cesare.
8 */
9 public class CaesarCipher
10 {
11 public static void main(String[] args) throws FileNotFoundException
12 {
13 final int DEFAULT_KEY = 3;
14 int key = DEFAULT_KEY;
15 String inFile =
16 String OUtFile =
17 int files = 0; // numero di argomenti sulla riga di comando che sono file
18
19 for (int i = 0; i < args.length; i++)
20 {
21 string arg = args[i];
22 if (arg.charAt(o) ==
23 {
24 // è un^opzione sulla riga di comando
25
26 char option = arg.charAt(l);
27 if (option == 'd') { key = -key; }
28 else { us ageO; return; }
29 }
30 else
31 {
32 // è il nome di un file
33
34 files++;
35 if (files == l) { inFile = arg; }
36 else if (files == 2) { outFile = arg; }
37 }
38 }
39 if (files != 2) { us ag eO ; return; }
40
41 Scanner in = new Scanner(new File(inFile));
42 in.useDelimiter('"'); // elabora singoli caratteri
43 PrintWriter out = new PrintWriter(outFile);
44
45 while (in.hasNextO)
46 {
47 char from = in.next().charAt(o);
48 char to = encrypt(from, key);
49 out.print(to);
50 }
51 in.closeO;
52 out.closeO;
53 }
54
55 /**
56 Cifra lettere maiuscole e minuscole traslandole in
57 base a una chiave.
58 @param eh la lettera da cifrare
59 @param key la chiave di cifratura
60 @return la lettera cifrata
594 C a p it o l o 11
*/
public static char encrypt(char ch, int key)
{
int base = 0;
if ('A' <= ch 8i& ch <= 'Z') { base = 'A'; }
else if ('a' <= ch && ch <= 'z') { base = 'a'; }
else { return ch; } // non è una lettera
int offset = ch - base + key;
final int LETTERS = 26; // numero di lettere nell'alfabeto romano
if (offset >= LE HE RS ) { offset = offset - LEHERS; }
else if (offset < 0) { offset = offset + LETTERS; }
return (char) (base + offset);
}
/**
Visualizza un messaggio che descrive la modalità di utilizzo.
*/
public static void usage()
{
System.out.println("Usage: java CaesarCipher [-d] infile outfile");
}
Auto-valutazione
1 1 . S e u n p r o g r a m m a v i e n e in v o c a t o c o n la rig a di c o m a n d o java CaesarCipher -d filel.txt,
q u a li s o n o g li e le m e n t i d i args?
12 . T e n e te tra ccia , p a sso d o p o passo, d e l l ’e s e c u z i o n e d e l p r o g r a m m a CaesarCipher, c o s ì c o m e
v i e n e in v o c a t o n e lla d o m a n d a p r e c e d e n te .
13. S e in v o c a to c o n la riga di c o m a n d o java CaesarCipher filel.txt file2.txt - d ,i l p r o g r a m m a
CaesarCipher fu n z io n a c o r r e tta m e n te ? P erch é?
14. C ifr a te la p arola C A E S A R u s a n d o il c ifr a rio d i C e sa r e.
15. C o m e si p u ò m o d ific a r e il p r o g r a m m a CaesarCipher in m o d o c h e l ’u te n te p ossa sp ec ific a re
u n a c h ia v e d i cifratu ra d iv ersa da 3 c o n u n ’o p z i o n e -k c o m e in q u e s t o e s e m p io ?
Perfarpratica
A q u e s t o p u n t o si c o n s i g l ia d i s v o l g e r e g li e s e r c iz i R l 1 .5 , E l i . 1 0 e E l i . 1 1 , al t e r m i n e
d e l c a p i t o lo .
Elaborarefileditesto
L’e la b o r a z i o n e d i f ile d i t e s t o c o n t e n e n t i d a ti rea li è u n p r o b le m a c h e p u ò r iv e la r si s o r
p r e n d e n t e m e n t e a r d u o : in q u e s ta s e z i o n e tr o v e r e te u n a g u id a in p iù fasi c h e v i p o tr à e sse r e
d ’a iu t o , u s a n d o d a ti r e la tiv i alla p o p o l a z i o n e d e l le n a z i o n i d e l m o n d o .
Afghanistan 50.56
Akrotiri 127.64
Albania 125.91
Algeria 14.18
American Samoa 288.92
Fase 1 C a p ite il p ro b le m a .
C i so n o tre o p z io n i:
U sa re a rg o m e n ti sulla rig a di c o m a n d o .
FileCountryValue.java
/**
Descrive un valore associato a una nazione.
*/
public class CountryValue
{
private String country;
private double value;
/**
In g r e s s o / u s c it a E g e s t io n e d e l l e e c c e z io n i 597
File PopulationDensity.jdva
1 import java.io.File;
2 import java.io.FileNotFoundException;
3 import java.io.PrintWriter;
4 import java.util.Scanner;
5
6 public class PopulationDensity
7 {
8 public static void main(String[] args) throws FileNotFoundException
9 {
10 // apri i file da leggere
11 Scanner ini = new Scanner(new File("worldpop.txt"));
12 Scanner in2 = new Scanner(new File("worldarea.txt"));
13
14 // apri il file da scrivere
15 PrintWriter out = new PrintWriter("world_pop_density.txt”);
16
17 // acquisisce righe da entrambi i file
18 while (inl.hasNextLine() && in2.hasNextLine())
19 {
20 CountryValue population = new CountryValue(inl.nextLine());
21 CountryValue area = new CountryValue(in2.nextLine());
22
23 // calcola e scrive la densità di popolazione
24 double density = 0;
25 if (area.getValueO I= O) // per evitare la divisione per zero
26 {
27 density = population.getValue() / area.getValue();
28 }
598 C a p it o l o 11
inl.closeO;
in2.close();
out.closeO;
^ 0 O A IS httpV^l^ww.t8^.9oy>OACT/b^byn^m^«/d^¢^d^«A^
Mate "ertiaTé
Rank Name Number Percent Name Number Percent
1 Ivfechael 4 ò: 06: 2 :006 Jessica ■o: oò: 1.543Ò
2 Christopher >01,250 1 750? <^shle, Oj I 70: 1.5372
3 Matthew 351.477 1.7119 Emily 237,133 1.2082
4 Joshua 328.955 1.6022 Sarah 224.000 1 1413
5 Jacob 298.016 14515 Samantha 223,913 1.1408
6 Nicholas 275,222 1.3405 Amanda 190,901 0.9726
7 Andrew 272.600 1.3277 Brittany 190,779 09720
8 Daniel 271.734 1.3235 Elizabeth 172.383 08783
9 Tyler 262.218 1.2771 Taylor 168,977 0.8609
10 Joseph 260.365 1 2681 Megan 160.312 0 8168
11 Brandon 259.299 12629 Hannah 158.647 08083
12 David 253.193 1 2332 Kayla 155.844 0.7940
1« A d b iock
• La p o s iz io n e (da 1 a 1000)
• Il n o m e , il n u m e r o di u tiliz z i e la p e r c e n tu a le di u tiliz z o d e l n o m e m a s c h ile c h e
o c c u p a tale p o siz io n e nella classifica
• Il n o m e , il n u m e ro di u tiliz z i e la p e r c e n tu a le di u tiliz z o d el n o m e fe m m in ile c h e
o c c u p a tale p o s iz io n e nella classifica
P er e se m p io , la rig a s e g u e n te :
Fase 1 C a p ite il p ro b le m a .
IotaIeMaschiIe = O
totaleFemminile = 0
Finché totaleMaschile < 50 oppure totaleFemminile < 50
Leggi una posizione e visualizzala.
Leggi il nome maschile, il suo conteggio e la sua percentuale.
Se totaleMaschile <5 0
Visualizza II nomeMaschile.
Aggiungi la percentualeMaschile al totaleMaschile.
Ripeti per il sesso femminile.
E c c o il c o d ic e d el m e to d o process:
/**
Legge una riga e visualizza il nome se il totale è inferiore al limite.
@param in il flusso di ingresso
*/
public void process(Scanner in)
{
In g r e s s o / u s c it a E g e s t io n e d e l l e e c c e z io n i 601
N e l se g u ito tro v a te il p ro g ra m m a c o m p le to .
D a te u n ’o c c h ia ta a c iò c h e v ie n e v isu a liz za to dal p ro g ra m m a : so lta n to 6 9 n o m i m a
schili e 153 n o m i fe m m in ili d a n n o c o n to di m e tà di tu tte le n ascite, d av v e ro u n a b u o n a
n o tiz ia p e r i p r o d u tto r i d i ac ce sso ri p e rso n a liz z a ti. L’E se rc iz io E l 1.12 vi c h ie d e rà di
stu d iare l’e v o lu z io n e di q u e sta d is trib u z io n e n e g li a n n i.
File BabyNames.java
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
File RecordReader.java
import java.util.Scanner;
/**
Costruisce un RecordReader con totale uguale a zero.
*/
public RecordReader(double aLimit)
{
total = 0;
limit = aLimit;
}
T lA ^ . ^ ^ .
E s is to n o d u e a s p e tti re la tiv i agli e r r o r i c h e a v v e n g o n o d u r a n te l ’e s e c u z io n e d i u n
p ro g ra m m a : i n d i v i d u a z i o n e e g e s t i o n e . A d e se m p io , il c o s tru tto r e di Scanner p u ò i n d i v i d u a r e
il te n ta tiv o di a c q u isiz io n e d i d ati m e d ia n te la le ttu ra di u n file in e siste n te , m a n o n è
in g ra d o di g e s t i r e tale e rro re : si p o tr e b b e ra g io n e v o lm e n te p o r re fin e all’e s e c u z io n e d el
p ro g ra m m a o p p u r e c h ie d e re all’u te n te di fo rn ire u n altro n o m e di file, e la classe Scanner
n o n è in g ra d o di sceg liere tra q u e s te a lte rn a tiv e , p e r cu i d ev e se g n ala re l’e rro re a u n a
z o n a d iversa d el p ro g ra m m a .
In Java, la g e s t i o n e d e l l e e c c e z i o n i { e x c e p t i o n h a n d l i n g ) è u n m e c c a n is m o v ersatile c h e
c o n s e n te di trasfe rire il c o n tro llo d e ll’e s e c u z io n e d el p ro g ra m m a dal p u n to in cu i v ie n e
se g n ala to l’e rro re a u n g e sto re ( h a n d l e r ) c h e sia in g ra d o di g estirlo . La p a rte re sta n te di
q u e s to c a p ito lo d isc u te in d e tta g lio tale m e c c a n ism o .
Figura 2
Una parte della gerarchia Throwable
delle classi di eccezioni.
Error Exception
f
ClassNot CloneNot Runtime
IOException Found Supported Exception
Exception Exception
EOF Arithmetic
Exception Exception
FileNot
ClassCast
Found
Exception
Exception
Illegal Number
MalformedURL
Argument C]- Format
Exception
Exception Exception
Illegal
UnknowHost
Exception State
Exception
IndexOut ArrayIndexOut
OfBounds 0- OfBounds
Exception Exception
NoSuch Input
Element O- Mismatch
Exception Exception
NullPointer
Exception
P e r g e s tir e e c c e z i o n i si u sa !’e n u n c i a t o t r y / c a t c h ,c h e va s c r it t o in u n a z o n a d e l p r o g r a m m a
c h e sa p p ia c o m e g e s t ir e u n a p a r t ic o la r e e c c e z i o n e . Il b l o c c o t r y c o n t i e n e e n u n c i a t i c h e
p o s s o n o p r o v o c a r e il l a n c io d i u n ’e c c e z i o n e d e l t i p o c h e si v u o l e g e s t ir e e d è s e g u i t o
d a c la u s o le c a tch , o g n u n a c o n t e n e n t e il g e s t o r e d i u n o s p e c i f i c o t i p o d i e c c e z i o n e . E c c o
u n e s e m p io :
try
{
String filename
606 C a p it o l o 11
} * ’ ’
catch (IOException exception)
{
exception.printStackTrace();
}
catch (NumberFormatException exception)
{
System.out.println(exception.getMessage());
}
exception.printStackTrace()
N e l s e c o n d o g e sto re di e c c e z io n e a b b ia m o in v o c a to exception.getMessage() p e r re c u p e ra re
il m essag g io associato all’e c c e z io n e : q u a n d o il m e to d o parseint lan cia NumberFormatException,
tale m e ssag g io c o n tie n e la strin g a da c u i n o n è stato p o ssib ile e s tra rre u n n u m e ro in te ro .
Q u a n d o la n c ia te u n ’e c c e z io n e p o te te a n c h e fo rn ire u n v o stro m essag g io . A d e se m p io , in
s e g u ito all’e s e c u z io n e di q u e s to e n u n c ia to
Sintassi try
{
enunciato
enunciato
}
catch {ClasseEccezione oggettoEccezione)
{
enunciato
enunciato
Esempio
Questo costruttore può lanciare
try FileNotFoundException.
try
{
File inFile = new File (filename);
Scanner in = new Scanner(inFile); // può lanciare FileNotFoundException
}
catch (FileNotFoundException exception) // l'eccezione viene catturata qui
{
Esempio
} * * *
11.4.4 Chiuderericorse
Q u a n d o si u tilizz a u n a riso rsa c h e n ecessita di u n ’a z io n e di c h iu su ra , c o m e u n o g g e tto
PrintWriter, b iso g n a fare a tte n z io n e alla p rese n za d i e c c e z io n i. A n alizz iam o q u esta se q u e n z a
di e n u n c ia ti:
} ’ ’ ’
Esempio
try (PrintWriter out = new PrintWriter(filename))
{ \
Questo codice può writeData(out);
Implementa !Interfaccia AutoCloseable.
lanciare eccezioni. }
Q u a n d o il b l o c c o tr y t e r m in a la p r o p r ia e s e c u z i o n e v i e n e i n v o c a t o il m e t o d o d o s e c o n la
v a r ia b ile d ic h ia r a ta n e l l ’i n t e s t a z io n e . S e n o n si è v e r ific a ta a lc u n a e c c e z i o n e q u e s t o a v v i e n e
al t e r m i n e d e l l ’e s e c u z i o n e d e l m e t o d o writeData; se , i n v e c e , è sta ta la n c ia ta u n ’e c c e z i o n e ,
il m e t o d o d o s e v i e n e i n v o c a t o p r im a c h e q u e s t a v e n g a tr a sfer ita al s u o g e s t o r e .
In u n e n u n c i a t o t r y c o n i n d i c a z i o n e d i r is o r s e si p o s s o n o d ic h ia r a r e p iù v a r ia b ili,
c o m e in q u e s t o e s e m p i o :
e c c e z i o n e . C o n s i d e r a t e u n c o n t o b a n c a r io : q u a n d o si te n t a d i p r e le v a r e u n a s o m m a
s u p e r io r e al s a ld o , v o l e t e s e g n a la r e u n ’e c c e z i o n e d i t i p o InsufficientFundsException.
Q u a n d o l ’e c c e z i o n e v i e n e c a ttu r a ta , la s tr in g a d e l m e s s a g g i o p u ò e sse r e r e c u p e r a ta u s a n d o
il m e t o d o getMessage d e lla c la s se Throwable.
AutQ-YdIutaziOiie
16. S e il v a lo r e d i b a la nc e è 1 0 0 e q u e l l o d i a m ou nt è 2 0 0 , c h e v a lo r e h a ba la nc e d o p o
l ’e s e c u z io n e d i q u e s t o fr a m m e n to di c o d ic e ?
if (amount > balance)
{
throw new IllegalArgumentException("Amount exceeds balance");
}
balance = balance - amount;
17. Q u a n d o si e ffe ttu a u n v e r s a m e n t o in u n c o n t o b a n c a r io n o n è p o s s ib ile c h e il c o n t o
“ vad a in r o s s o ’’, c io è c h e il sa ld o d iv e n ti n e g a tiv o , a m e n o c h e la s o m m a versata n o n sia
n e g a tiv a . S c r iv e te u n e n u n c ia t o c h e , in tal ca so , la n c i u n ’e c c e z i o n e ap p ro p ria ta .
1 8 . D a t o il m e t o d o s e g u e n te :
public static void main(String[] args)
{
612 C a p it o l o 11
try
{
Scanner in = new Scanner(new File("input.txt"));
int value = in.nextlnt();
System.out.printIn(value);
}
catch (lOException exception)
{
System.out.printlnC'Error opening file.");
}
}
Perfarpratica
A q u e s t o p u n t o si c o n s i g l ia d i s v o lg e r e g li e s e r c iz i R l 1 .8 , R l 1 .9 e R l 1 . 1 0 , al t e r m i n e
d e l c a p it o lo .
I Suflgerirofinti p e r l a P t Q f l r a m m a z i f l n e 1 1 . 1
d i r is o lv e r e ,
s o l i t a m e n t e è m e g l i o la n c ia r e u n ’e c c e z i o n e p i u t t o s t o c h e c e r c a r e d i m e t t e r e in a t t o u n a
s o l u z i o n e i m p r e c is a o i n c o m p l e t a . I m m a g i n a t e , a d e s e m p i o , c h e u n m e t o d o p r e v e d a
d i l e g g e r e u n n u m e r o d a u n f i l e e c h e il f i le , i n v e c e , n o n c o n t e n g a u n n u m e r o : u s a r e
s e m p l i c e m e n t e il v a l o r e z e r o s a r e b b e u n a p e s s im a i d e a , p e r c h é si n a s c o n d e r e b b e il
p r o b l e m a r e a le e , p r o b a b i l m e n t e , si p r o v o c h e r e b b e u n d i v e r s o p r o b l e m a in u n a ltr o
p u n to d el p rogram m a.
try
{
Scanner in = new Scanner(new File(filename));
// il compilatore protestava per FileNotFoundException
}
catch (FileNotFoundException e) {} // ecco fatto!
Aisimifinti avaiizatill,______________
r Asserzioni
Un’asserzione (assertion) è u n a c o n d iz io n e c h e il p ro g ra m m a to re ritie n e d e b b a essere
v era in qualsiasi m o m e n to in u n a sp ecifica p o siz io n e d el p ro g ra m m a . U n a v e rific a di
asserzio n e (assertion check) c o n tro lla c h e u n ’asserzio n e sia e ffe ttiv a m e n te vera. E c c o u n
e s e m p io tip ic o :
In q u e s t o m e t o d o il p r o g r a m m a t o r e si a sp e tta c h e la q u a n t it à d i d e n a r o v e rsa ta n o n p o s sa
m a i e s s e r e n e g a t iv a . Q u a n d o ta le a s s e r z io n e è v e r if ic a t a n o n s u c c e d e n u lla e il p r o g r a m m a
o p e r a n e l m o d o c o n s u e t o . S e , p e r q u a l c h e r a g io n e , q u e s t o n o n è v e r o e la v e r if ic a d i
a s s e r z io n i d u r a n t e l ’e s e c u z i o n e è stata a b ilita , a llo r a l ’e n u n c i a t o assert la n c ia u n ’e c c e z i o n e
d i t i p o AssertionError, f a c e n d o t e r m in a r e il p r o g r a m m a .
S e , p e r ò , la v e r if ic a d i a s s e r z io n i è d is a b ilita ta , l ’a s s e r z io n e n o n v i e n e c o n t r o lla t a e il
p r o g r a m m a f u n z i o n a alla m a s s im a v e lo c i t à , c o s a c h e a v v i e n e p e r i m p o s t a z i o n e p r e d e f in it a .
P e r e s e g u ir e u n p r o g r a m m a c o n la v e r if ic a d e lle a s s e r z io n i a b ilita ta , si u sa q u e s t o
com ando:
I n v e c e d e l l ’o p z i o n e -enableassertions si p u ò a n c h e u sa re l ’a b b r e v i a z i o n e -ea. La v e r if ic a
d e l l e a s s e r z io n i d o v r e b b e e s s e r e a b ilita ta d u r a n t e le fa si d i s v i l u p p o e c o l l a u d o d e l
program m a.
tr y
{
}
finally
{
// questo codice viene eseguito in ogni caso
}
S e il c o r p o d e l l ’e n u n c i a t o try v i e n e e s e g u i t o s e n z a c h e si v e r i f ic h i u n ’e c c e z i o n e , v e rr à
p o i e s e g u i t o il b l o c c o finally. S e , i n v e c e , v i e n e la n c ia ta u n ’e c c e z i o n e , il b l o c c o finally v i e n e
e s e g u i t o p r im a c h e q u e s t a v e n g a p r o p a g a ta al p r o p r io g e s t o r e .
L’u t i l iz z o d e l l ’e n u n c i a t o try/finally è c o m u n q u e a b b a s ta n z a raro, p e r c h é la m a g g i o r
p a r te d e l le c la ssi d e lla lib r e r ia d i Java c h e n e c e s s i t a n o d i a z io n i d i c h iu s u r a i m p l e m e n t a n o
l ’in t e r f a c c ia AutoCloseable.
In g r e s s o / u s c i t a E g e s t io n e d e l l e e c c e z io n i 615
3
1.45
- 2.1
0.05
File DdtaAnalyzer.java
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;
try
{
Sy stem. out. print In ("Please enter the file name: ");
String filename = in.next();
S e il f ile n o n e s is t e o se c o n t i e n e d a ti in f o r m a t o e r r a to , le c la u s o le catch p r e s e n t i n e l
m e t o d o main g e n e r a n o u n a s e g n a l a z io n e d ’e r r o r e c o m p r e n s i b i le p e r l ’u t e n t e .
Il s e g u e n t e m e t o d o readFile d e lla c la s se DataSetReader c o s t r u i s c e u n o g g e t t o d i t i p o
Scanner e i n v o c a il m e t o d o readData, d is in t e r e s s a n d o s i c o m p l e t a m e n t e d e l le e c c e z i o n i : s e si
v e r if ic a u n p r o b le m a d u r a n t e la le ttu r a d e l file , l ’e c c e z i o n e v i e n e s e m p l i c e m e n t e tr a sfer ita
a ll’i n v o c a n t e .
N e l l a p r o p r ia c la u s o la throws, il m e t o d o d ic h ia r a d i p o t e r la n c ia r e lOException, c h e è la
s u p e r c la s s e c o m u n e d i FileNotFoundException ( la n c ia ta d a l c o s t r u t t o r e d i Scanner) e d i
BadDataException (la n c ia ta d al m e t o d o readData).
V e d ia m o o r a il m e t o d o readData d e lla c la s se DataSetReader: l e g g e il n u m e r o d i v a lo r i,
c o s t r u i s c e u n array e in v o c a readValue p e r c ia s c u n v a lo r e d a l e g g e r e .
if (in.hasNextO)
{
throw new BadDataException("End of file expected");
}
}
Q u e s t o m e t o d o c o n t r o lla d u e p o t e n z i a li e r r o r i: il f ile p o t r e b b e n o n in iz ia r e c o n u n
n u m e r o in t e r o o p p u r e p o t r e b b e a v e re u lt e r i o r i r ig h e d o p o c h e tu t t i i v a lo r i s o n o stati le t t i.
T u tta v ia , q u e s t o m e t o d o n o n te n t a in a lc u n m o d o d i c a ttu r a r e e c c e z i o n i . I n o lt r e , s e il
m e t o d o readValue la n c ia u n ’e c c e z i o n e , c o s a c h e a v v e r r à se il f ile n o n c o n t i e n e u n n u m e r o
s u f f ic i e n t e d i v a lo r i, q u e s ta v i e n e s e m p l i c e m e n t e tr a sfe r ita a c h i h a i n v o c a t o il m e t o d o .
E c c o il m e t o d o readValue:
P e r v e d e r e a ll’o p e r a la g e s t i o n e d e l le e c c e z i o n i , d i a m o u n ’o c c h ia t a a u n o s p e c i f i c o
s c e n a r io d ’e r r o r e .
1. DataAnalyzer.main i n v o c a DataSetReader.readFile.
2. readFile i n v o c a readData.
3. readData i n v o c a readValue.
4. readValue n o n tro v a il v a lo r e a t t e s o e la n c ia BadDataException.
5. readValue n o n h a g e s t o r i p e r ta le e c c e z i o n e e t e r m i n a i m m e d i a t a m e n t e la p r o p r ia
e s e c u z io n e .
6. readData n o n h a g e s t o r i p e r t a le e c c e z i o n e e t e r m i n a i m m e d i a t a m e n t e la p r o p r ia
e s e c u z io n e .
7. readFile n o n h a g e s t o r i p e r t a le e c c e z i o n e e t e r m i n a i m m e d i a t a m e n t e la p r o p r ia
e s e c u z i o n e , d o p o a v e r c h i u s o l ’o g g e t t o d i t i p o Scanner.
8. DataAnalyzer.main h a u n g e s t o r e p e r l ’e c c e z i o n e BadDataException c h e v is u a liz z a u n
m e s s a g g i o p e r l ’u t e n t e , c o n s e n t e n d o d i n u o v o la p o s s ib ilit à d i in s e r ir e il n o m e d i u n
file ; n o t a t e c h e g li e n u n c ia t i c h e c a lc o l a n o la s o m m a d e i v a lo r i n o n s o n o stati e s e g u it i.
Q u e s t o e s e m p i o m o s t r a la s e p a r a z io n e tra l ’i n d i v i d u a z i o n e d e l l ’e r r o r e ( n e l m e t o d o
DataSetReader.readValue) e la su a g e s t i o n e ( n e l m e t o d o DataAnalyzer.main). In m e z z o si
618 C a p it o l o 11
File DdtaSetReader.java
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
*/
public class DataSetReader
{
private double[] data;
/*
Legge un insieme di dati.
@param filename il nome del file che contiene i dati
@return i dati presenti nel file
*/
public double[] readFile(String filename) throws IOException
{
File inFile = new File(filename);
try (Scanner in = new Scanner(inFile))
{
readData(in);
return data;
}
}
/*
Legge tutti i valori.
@param in lo Scanner con cui si acquisiscono i valori
if (in.hasNextO)
{
throw new BadDataException("End of file expected");
}
}
Legge un valore.
@param in lo Scanner con cui si acquisiscono i valori
@param i la posizione in cui memorizzare il valore
*/
private void readValue(Scanner in, int i) throws BadDataException
{
if (!in.hasNextDoubleO)
{
throw new BadDataException("Data value expected");
}
data[i] = in.nextDoubleO;
}
}
File BadDataExceptionJava
import java.io.IOException;
/**
Questa classe segnala un errore nei dati in ingresso.
*/
public class BadDataException extends IOException
{
public BadDataExceptionO {}
public BadDataException(String message)
{
super(message);
}
}
JujtfiLryalutdzjQJie
24. Perché il metodo DataSetReader.readFile non cattura alcuna eccezione?
25. Seguite passo dopo passo il flusso di esecuzione nel caso in cui Putente specifichi un file
che esiste ma è vuoto.
26. Come si modificherebbe !’implementazione se, quando non è possibile acquisire un numero
in virgola mobile, il metodo readValue lanciasse un’eccezione NoSuchElementException
invece di BadDataException?
27. Cosa succede all’oggetto Scanner se il metodo readData lancia un’eccezione?
28. Cosa succede all’oggetto Scanner se il metodo readData non lancia un’eccezione?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R l 1.16, R l 1.17 e El 1.13, al termine
del capitolo.
620 C a p it o l o 11
A e s o c ie tU 1 2
Llncidente del razzo Ariane sori. Gli stessi sistemi di riferimento che tali dispositivi potessero guastarsi
L’Agenzia Spaziale Europea (Euro inerziali e lo stesso software per il per motivi meccanici e la probabilità
pean Space Agency, ESA), la contro calcolatore avevano funzionato bene che i due dispositivi avessero lo stesso
parte europea della NASA, sviluppò per il predecessore. Ariane 4. guasto meccanico era considerata
un modello di missile denominato Tuttavia, a causa di modifiche al assai remota. A quel punto il razzo
Ariane, usato più volte con successo progetto del missile, uno dei sensori era privo di informazioni affidabili
per lanciare satelliti e per svolgere misurò una forza di accelerazione sulla propria posizione e andò fuori
esperimenti scientifici nello spazio. maggiore di quella che si riscontrava rotta.
nell’Ariane 4. Tale valore, espresso Sarebbe forse stato meglio che
Tuttavia, quando una nuova versione.
come numero in virgola mobile, era il software non fosse stato così di
Ariane 5, fu lanciata il 4 giugno 1996
memorizzato in un numero intero ligente? Se avesse ignorato l’errore
dal sito di lancio dell’ESA a Kourou,
a 16 bit (come una variabile di tipo numerico di trabocco, il dispositivo
nella Guyana Francese, il missile virò
short in Java). Diversamente da Java, il non si sarebbe spento, avrebbe sem
dalla sua rotta circa 40 secondi dopo
linguaggio Ada usato per il software plicemente elaborato dati errati. Ma
la partenza. Volando a un angolo di
di quei dispositivi genera un’eccezio in tal caso il sensore avrebbe segna
più di 20 gradi, anziché verticalmen ne se un numero in virgola mobile lato errati valori di posizione, cosa
te, si esercitò su di esso una tale forza troppo grande viene convertito in che sarebbe stata altrettanto fatale.
aerodinamica che i razzi propulsori si un intero, ma, sfortunatamente, i pro Al contrario, una implementazione
staccarono dal missile, innescando il grammatori avevano deciso che tale corretta avrebbe dovuto catturare
meccanismo automatico di auto-di situazione non sarebbe mai accaduta l’errore di trabocco, fornendo una
struzione: il missile si fece esplodere. e non avevano definito un gestore strategia per ricalcolare i dati di volo.
La causa che innescò questo in per l’eccezione. Ovviamente, in questo contesto
cidente fu un’eccezione non gestita! Quando avvenne il trabocco nu “lasciar perdere” non era una scelta
Il missile conteneva due dispositivi merico, venne lanciata l’eccezione e, ragionevole.
identici (chiamati sistemi di riferi poiché non vi era un gestore, il dispo Il vantaggio del meccanismo di
mento inerziali) che elaboravano dati sitivo si spense. Il computer di bordo gestione delle eccezioni sta nel fatto
di volo provenienti da dispositivi di rilevò il guasto e interrogò il sensore che rende questi problemi espliciti
misura e li trasformavano in infor di riserva, che, però, si era spento per per i programmatori, una cosa a cui
mazioni riguardanti la posizione del lo stesso identico motivo, una cosa dovete pensare quando state maledi
missile, usate poi dal computer di che i progettisti del missile non ave cendo il compilatore Java perché si
bordo per controllare i razzi propul vano previsto: avevano immaginato lamenta di eccezioni non catturate.
• Se una stringa contiene le cifre di un numero, se ne può ottenere il valore invocando Integer,
parseint oppure Double.parseDouble.
Esercizi di riepilogo
R l 1.1. Cosa succede se cercate di aprire in modalità di lettura un file inesistente? Che cosa succede
se cercate di aprire in modalità di scrittura un file inesistente?
R l 1.2. Cosa succede se cercate di aprire un file per scrivere, ma il file o il dispositivo sono protetti
contro la scrittura (cioè sono risorse talvolta definite “a sola lettura”, read-only)? Fate un esperimento
con un breve programma di prova.
R l 1.3. Cosa succede se scrivete dati in un PrintWriter senza chiuderlo? Progettate un programma
di prova e dimostrate come si possano perdere dati.
R l 1.4. Come si apre un file il cui nome contiene una barra rovesciata, come c:\temp\output.dat?
R l 1.5. Se il programma Woozle viene eseguito con il comando
java Woozle -Dname=piglet -INeeyore -v heff.txt a.txt lump.txt
R l 1.9. Quando un programma esegue un enunciato throw, qual è il successivo enunciato che
viene eseguito?
Rl 1.10. Cosa succede se non esiste la clausola catch corrispondente a un’eccezione che viene
lanciata?
★★ R l 1.11. Cosa può fare un programma con l’oggetto eccezione ricevuto da una clausola catch?
★★ Rl 1.12. 11 tipo dell’oggetto eccezione è sempre uguale al tipo dichiarato nella clausola catch che
lo cattura? In caso di risposta negativa, perché?
★
R II.13. A cosa serve l’enunciato try con indicazione di risorse? Fornite un esempio di utilizzo.
★★ Rl 1.14. Cosa succede quando in un enunciato try con indicazione di risorse viene lanciata
un’eccezione, quindi viene invocato il metodo dose e questo, a sua volta, lancia un’eccezione
diversa dalla prima? Quale eccezione viene catturata da una clausola catch circostante? Scrivete
un programma d’esempio e provate.
Rl 1.15. Quali eccezioni possono essere lanciate dai metodi next e nextint della classe Scanner?
Sono eccezioni a controllo obbligatorio oppure no?
Rl 1.16. Supponete che il programma visto nel Paragrafo 11.5 legga un file contenente questi
valori:
1
2
3
4
In g r e s s o / u s c it a E g e s t io n e d e l l e e c c e z io n i 623
Quale sarà il risultato? Come si può migliorare il programma per fare in modo che fornisca una
più accurata segnalazione degli errori?'
R l 1.17. Il metodo readFile visto nel Paragrafo 11.5 può lanciare un’eccezione di tipo NullPoin-
terException? Se sì, in quale situazione?
★★★ R l 1.18. Il codice seguente cerca di chiudere il flusso in cui scrive senza usare un enunciato try
con indicazione di risorse:
PrintWriter out = new PrintWr iter (filename);
try
{
. . . // scrittura dei dati attraverso out
out.closeO :
}
catch (lOException exception)
{
out.closeO :
}
Identificate Io svantaggio di questo approccio. Su^erimentcr. cosa succede se il costruttore di
PrintWriter o il metodo d o s e lancia un’eccezione?
giercizi dijisgjMDUòsofi.
El 1.1. Scrivete un programma porti a termine i seguenti compiti:
Apri un file di nome hello.txt.
Memorizza nel file il messaggio "Hello, World!".
Chiudi il file.
Apri di nuovo lo stesso file.
Leggi il messaggio memorizzandolo in una stringa e visualizzalo.
El 1.2. Scrivete un programma che legga un file, elimini le righe vuote e scriva le righe non vuote
nel file originario, sovrascrivendolo.
E11.3. Scrivete un programma che legga un file, elimini le righe vuote che si trovano all’inizio e
alla fine e scriva le righe rimanenti nel file originario, sovrascrivendolo.
El 1.4. Scrivete un programma che legga un file, una riga per volta, scrivendola nel file di uscita
preceduta dal numero di riga. Se il file letto è:
Mary had a little lamb
Whose fleece was white as snow.
And everywhere that Mary went,
The lamb was sure to go!
I numeri di riga devono essere racchiusi tra i delimitatori /* e */, in modo che il programma possa
essere utilizzato per numerare le righe di un file sorgente Java.
Il programma deve chiedere all’utente i nomi dei file di ingresso e di uscita.
El 1.5. Risolvete di nuovo l’esercizio precedente consentendo all’utente di specificare il nome
dei file sulla riga dei comandi. Se l’utente non lo fa, il programma deve chiedere il nome del file
durante l’esecuzione.
El 1.6. Scrivete un programma che legga un file contenente due colonne di numeri in virgola
mobile, per poi visualizzare il valore medio di ciascuna colonna. Chiedete all’utente di fornire il
nome del file.
El 1.7. Scrivete un programma che chieda all’utente il nome di un file e, poi, ne visualizzi il
numero di caratteri, di parole e di righe.
El 1.8. Scrivete un programma Find che effettui una ricerca in tutti i file specificati sulla riga dei
comandi e visualizzi tutte le righe contenenti una parola specifica, fornita come primo argomento
sulla riga dei comandi. Per esempio, se eseguite
java Find ring report.txt address.txt Homework.java
il p r o g r a m m a potrebbe stampare
El 1.9. Scrivete un programma che controlli l’ortografia di tutte le parole presenti in un file. Deve
leggere ciascuna parola del file e controllare se è presente in un elenco di parole, disponibile nei
sistemi Macintosh e Linux nel file /usr/share/dict/words (e facilmente reperibile anche in Internet).
II programma deve visualizzare tutte le parole del file che non sono presenti nell’elenco.
El 1.10. Scrivete un programma che sostituisca in un file ciascuna riga con la sua inversa. Per
esempio, se eseguite
java Reverse HelloPrinter.java
al termine della sua esecuzione il file output.txt dovrà avere questo contenuto:
The lamb was sure to go.
And everywhere that Mary went
Whose fleece was white as snow
Mary had a little lamb
E l 1 . 12. Recuperate i dati relativi ai nomi usati nei decenni scorsi dal sito della Social Security
Administration, inserendoli in file di nome babynames80s.txt e così via. Modificate il programma
BabyNames. java visto nella sezione Esempi completi 11.1 in modo che chieda all’utente il nome di
un file. I numeri all’interno dei file sono separati da virgole, quindi dovete modificare il programma
in modo che gestisca questo formato. Siete in grado di individuare una tendenza, osservando le
frequenze?
E l 1.1 3 .Scrivete un programma che chieda all’utente di inserire un insieme di valori in virgola
mobile. Quando viene inserito un valore che non è un numero, date all’utente una seconda possi
bilità di introdurre un valore corretto e, dopo due tentativi, fate terminare il programma. Sommate
tutti i valori che sono stati specificati in modo corretto e, quando l’utente ha terminato di inserire
dati, visualizzate il totale. Usate la gestione delle eccezioni per identificare i valori di ingresso non
validi.
Modificate la classe BankAccount in modo che lanci IllegalArgumentException quando viene
E l 1 .1 4 .
costruito un conto con saldo negativo, quando viene versata una quantità di denaro negativa o
quando viene prelevata una somma che non sia compresa tra 0 e il saldo del conto. Scrivete un
programma di collaudo che provochi il lancio di tutte e tre le eccezioni, catturandole.
E l 1 .15. Ripetete l’esercizio precedente, ma lanciate eccezioni di tre tipi definiti da voi.
Modificate la classe DataSetReader in modo che non invochi hasNextInt né hasNextDouble.
E l 1.1 6 .
Semplicemente, lasciate che nextint e nextDouble lancino un’eccezione di tipo NoSuchElementException,
catturandola nel metodo main.
Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
1 2
Ricorsione
12.1 Numeritrianaolari
Iniziamo questo capitolo con un esempio estremamente semplice che mette ben in
evidenza la potenza del pensiero ricorsivo. In questo esempio considereremo forme
triangolari come queste:
[]
[][]
[][][]
} ’ ' '
[]
[][]
[][][]
[][][][]
Supponiamo di conoscere l’area, smallerArea, del triangolo più piccolo, formato dalle
prime tre righe: il terzo numero triangolare. Potremo quindi calcolare facilmente l’area
R ic o r s io n e 629
del triangolo più grande, formato da tutte le quattro righe, in questo modo:
smallerArea + wicith
Come possiamo calcolare l’area del triangolo più piccolo? Costruiamo un triangolo più
piccolo e chiediamogliela!
public int ge tA re aO
{
if (width == I) { return l; }
Triangle smallerTriangle = new Triangle(width - l);
int smallerArea = smallerTriangle.getArea();
return smallerArea + width;
}
Ecco una schematizzazione di ciò che accade quando calcoliamo l’area del triangolo di
Un'elaborazione ricorsiva risolve
un problema usando la soluzione
ampiezza 4.
H problema stesso nel caso di dati
di ingresso più semplici. • Il metodo g e tA re a crea un triangolo più piccolo di ampiezza 3.
• Invoca getA re a su tale triangolo.
• Quel metodo crea un triangolo più piccolo di ampiezza 2.
• Invoca ge tArea su tale triangolo.
• Quel metodo crea un triangolo più piccolo di ampiezza 1.
• Invoca getA re a s u tale triangolo.
• Tale metodo restituisce 1.
• Il metodo restituisce s m a l l e r A r e a + width = 1 + 2 = 3.
• Il metodo restituisce sm a l l e r A r e a + w i dt h = 3 + 3 = 6.
• Il metodo restituisce sm a l l e r A r e a + width = 6 + 4 = 10.
Questa soluzione presenta un aspetto interessante: per risolvere il problema del calcolo
dell’area di un triangolo di ampiezza assegnata, usiamo il fatto che possiamo risolvere lo
stesso problema per un triangolo di ampiezza minore. Questa viene chiamata soluzione
ric o Y sìv a .
grande.
Esistono due requisiti che sono basilari per il corretto funzionamento di una ricorsione:•
Il metocio getArea invoca se stesso con valori di ampiezza sempre più piccoli: prima o poi
una ricorsione termini, devono i
esistere casi speciali per i dati ì
l’ampiezza diventa uguale a 1, che è un caso speciale per il calcolo dell’area di un triangolo,
in ingresso più semplici. ;
che vale 1. 11 metodo getArea, quindi, riesce sempre a concludere la propria elaborazione.
In realtà occorre fare molta attenzione: cosa succede se chiedete l’area di un triangolo
di ampiezza —IPViene calcolata l’area di un triangolo di ampiezza —2,la quale operazione
richiede il calcolo dell’area di un triangolo di ampiezza —3, e così via. Per evitare ciò, il
metodo getArea dovrebbe restituire 0 se l’ampiezza non è maggiore di zero.
La ricorsione non è assolutamente necessaria per calcolare numeri triangolari. L’area
di un triangolo è uguale a questa somma:
1 + 2 + 3 + ... + width
Molte ricorsioni semplici possono essere calcolate con cicli, ma per molte ricorsioni
complesse, come quella del nostro prossimo esempio, i cicli equivalenti possono essere
complessi.
In realtà, in questo caso non avete nemmeno bisogno di un ciclo per calcolare la
risposta. La somma dei primi n numeri interi si può calcolare con questa formula:
FileTriangle.javd
/**
Costruisce una forma triangolare.
@param aWidth l'ampiezza (e l'altezza) del triangolo
R ic o r s io n e 631
*/
public Triangle(int aWidth)
{
width = aWidth;
}
FileTriangleTester.java
public class TriangleTester
{
public static void main(String[] args)
{
Triangle t = new Triangle(lO);
int area = t.getArea();
System.out.println("Area: " + area);
System.out.printIn("Expected: 55");
}
}
1. Perché nella versione finale del metodo getArea !’enunciato else if (width == i) { return
I; } non è necessario?
2. Come modifichereste il programma per fare in modo che calcoli ricorsivamente l’area
di un quadrato?
3. In alcune culture i numeri che contengono la cifra 8 sono ritenuti fortunati. Cosa c’è di
sbagliato nel metodo seguente, che vorrebbe verificare se un numero è fortunato?
public static boolean isLucky(int number)
{
int IastDigit = number % 10;
if (lastDigit == 8) { return true; }
632 C a p it o l o 12
else
{
return isLucky(number / io); // verifica il numero senza l'ultima cifra
4. Per calcolare una potenza di 2 si può raddoppiare il valore della potenza avente esponente
immediatamente inferiore. Ad esempio, se volete calcolare 2^’ e conoscete il valore di 2’^^
= 1024, potete scrivere che 2’*= 2 x 2’^ = 2 x 1024 = 2048. Scrivete il metodo ricorsivo
public static int pow2(int n) basato su questa osservazione.
5. Analizzate il seguente metodo ricorsivo:
public static int mystery(int n)
{
if (n <= o) { return 0; }
else
{
int smaller = n - l;
return mystery(smaller) + n * n;
}
}
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E 12.1, E 12.2 e E l2.12, al termine del
capitolo.
ricorsivo, il programma si arresta non appena tale linea di codice viene raggiunta durante
l’esecuzione di una qualsiasi delle invocazioni del metodo ricorsivo. Supponete di voler effettuare
il debugging del metodo ricorsivo getArea della classe Triangle, eseguendo il programma
TriangleTester con il debugger fino a quando si ferma, all’inizio del metodo getArea.
Ispezionate la variabile di esemplare width: vale 10.
Rimuovete il punto di arresto e, ora, eseguite il programma fino al raggiungimento
dell’enunciato return smallerArea + width; (come nella Figura l).Se controllate nuovamente
width, il suo valore è 2! Non ha senso: non c’era nessuna istruzione che modificasse il
valore di width! È forse un errore del programma di debugging?
Figura 1
EUe Edit Source {levigete Setrch Ero^cct gun window Help
Debugging di un m etodo
)rt- O - V''I I B I Debugjy Ja**
^vanabte> Vh G Debug a ^ X
Name value ▼tZj THangIeTester [Java Application]
▼• thli I THartgIe (id>l6) ▼M THangIeTester at U>calhost:S924d
« width Iz ▼^ Thread [main] (Suspended (breakpoint at Une ì ì In THangIe))
A O smaUerThangle ! THangle (Id -IT ) ^ THanglc.9etArea() Une: 33
smaUerArea |i S THangIc.getAreaO line: 32
= THangIc.getAreaO Unr. 32
S THangtc.getAieaO line: 32
(21 THangIe java a "□ S THangIc.getAreaO linr. 32
pubUc in t getAreaO S THangIc.getAreaO Unr. 32
{
i f (width <« • ) { return • ; > S TViangIc.gctAreaO Une: 32
else i f (width I ) { return I; ) 3 THangIc.getAreaO Unr. 32
a lte
( S THangle.getAreaO Unr. 32
Trian gle saalIe r T r I angle ■ new Trlangle(wld1 3 THanglcTestcr.ma{n(Stf Ing(I) Unr. 6
In t S M lle rA rea ■ S M lle rT rla n g le .g e tA re a O
return taa lle rA re a * w idth; fl J /data/apps/]dk1.7.0_04/bi(V]ava (12 Juin 2012 ld;03:St)
>
)
-jaa.
No, non Io è: il programma si è arrestato alla prima invocazione ricorsiva di getArea che
ha raggiunto l’enunciato return. Se vi sentite confusi, osservate la pila dell e i n v o c a z i o n i
{cali stack, a destra nella Figura 1): vedrete che vi sono, in attesa (pending), nove invocazioni
di getArea.
Si può usare il debugger anche con metodi ricorsivi: bisogna soltanto stare
particolarmente attenti e dare un’occhiata alla pila delle invocazioni per capire in quale
delle invocazioni ci si trova.
Consigli praticll2.1 .
Pensare ricorsivam ente
Per risolvere un problema ricorsivamente serve una diversa impostazione mentale rispetto
alla soluzione realizzata mediante la programmazione di cicli. In effetti, la cosa risulta più
facile se siete un po’ pigri (o fate finta di esserlo) e vi piace che altri facciano la maggior
parte del vostro lavoro. Se dovete risolvere un problema complesso, risolvete il problema
nei casi più semplici e immaginate che “qualcun altro” faccia la maggior parte del lavoro
difficile e pesante. Dovete, poi, solamente capire come trasformare le soluzioni dei casi
semplici in una soluzione per il problema completo. Per illustrare la tecnica della ricorsione,
consideriamo il seguente problema.
634 C a p it o l o 12
Problem a. Vogliamo verificare se una frase è un palindromo (cioè una stringa che è uguale
a se stessa quando se ne inverte l’ordine dei caratteri). Esempi classici di palindromi sono
Tutti questi dati di ingresso più semplici sono plausibili per la verifica di un palin
dromo.
R ic o r s io n e 635
Fase 2 Combinate le soluzioni dei casi più semplici per risolvere il problema originario
Immaginate di aver ottenuto le soluzioni del vostro problema nei casi più semplici, così
come li avete individuati nella Fase 1. Non preoccupatevi di come si ottengano queste
soluzioni, confidate semplicemente nella loro effettiva disponibilità. Dite a voi stessi:
questi sono casi più semplici, per cui qualcun altro risolverà questi problemi al posto mio.
Pensate ora a come poter trasformare la soluzione di questi casi più semplici in una
soluzione per i dati a cui state pensando veramente. Può darsi che dobbiate aggiungere
una piccola quantità, corrispondente alla piccola quantità che avete eliminato per arrivare
al caso più semplice, oppure può darsi che abbiate diviso il problema originario a metà e
che abbiate le soluzioni per le due parti, nel qual caso dovete comporre le due soluzioni
per arrivare alla soluzione completa.
Considerate i metodi per semplificare i dati in ingresso nella verifica di un palindromo.
Dividere la stringa a metà non sembra essere una buona idea. Se dividete
"Madam, I'm Adam"
'"m Adam"
Nessuna delle due è un palindromo: dividere i dati a metà e verificare se ciascuna parte
sia un palindromo sembra portare in un vicolo cieco.
La semplificazione più promettente è Peliminazione del primo e dell’ultimo carattere.
Eliminando la M all’inizio e la m alla fine, si ottiene
"adam, I'm Ada"
Supponete di poter verificare se tale stringa più corta sia un palindromo: di conseguenza,
ovviamente, la stringa originaria è un palindromo, dato che abbiamo tolto la stessa lettera
all’inizio e alla fine. Molto promettente: quindi, possiamo dire che una parola è un
palindromo se
Di nuovo, non preoccupatevi di come funzioni la verifica per la stringa più corta:
semplicemente, funziona.
C ’è, però, un altro caso da considerare: cosa succede se la prima o l’ultima lettera della
parola non è, in realtà, una lettera? Ad esempio, la stringa
636 C a p it o l o 12
termina con il carattere !, che non è uguale al carattere A iniziale. Sappiamo, però, che
quando verifichiamo se una stringa è un palindromo dobbiamo ignorare tutti i caratteri
che non sono lettere, quindi, quando !’ultimo carattere non è una lettera ma il primo
carattere lo è, non ha senso eliminare sia il primo che !’ultimo carattere. Questo non è un
problema: eliminate semplicemente l’ultimo carattere. Se la stringa più corta che rimane
è un palindromo, allora rimane un palindromo anche quando le attaccate un carattere
che non sia una lettera.
Lo stesso ragionamento si può applicare se è il primo carattere a non essere una
lettera. Abbiamo, quindi, un insieme completo di casi.
• Se il primo e l’ultimo carattere sono lettere, verificate se sono uguali. In tal caso,
eliminateli entrambi e verificate la stringa rimanente.
• Altrimenti, se !’ultimo carattere non è una lettera, eliminatelo e verificate la stringa
rimanente.
• Altrimenti, il primo carattere non è una lettera: eliminatelo e verificate la stringa
rimanente.
In tutti i tre casi potete usare la soluzione del problema più semplice per risolvere il
vostro problema iniziale.
Non è necessario identificare una soluzione speciale per le stringhe di due caratteri: la
Fase 2 si applica anche a loro, rimuovendo entrambi i caratteri o uno solo di essi, in base
alle regole viste. Dobbiamo, invece, decidere cosa fare con le stringhe di lunghezza 0 e
1: in tali casi la Fase 2 non può essere applicata, semplicemente perché non ci sono due
caratteri da eliminare.
La stringa vuota è un palindromo, in quanto è identica a se stessa quando la si legge
al contrario. Se pensate che questo ragionamento sia troppo artificioso, considerate la
stringa "mm". Secondo la regola identificata nella Fase 2, questa stringa (che è certamente
un palindromo) è un palindromo se il primo e l’ultimo carattere sono uguali e se la parte
R ic o r s io n e 637
Esempi compleJll2J
Ù Ricerca di file
Problema. Dovete visualizzare i nomi di tutti i file appartenenti a un albero di cartelle e
aventi una determinata estensione (che è la parte terminale del nome). Per risolvere questo
problema, vi servono due metodi della classe File. Un oggetto File può rappresentare una
cartella (directory) o un semplice file, e il metodo
boolean isDirectoryO
File[] IistFilesO
restituisce un array contenente tutti gli oggetti File contenuti in una cartella: possono
essere semplici file o altre cartelle.
• Considerare ciascun file presente nella cartella che si trova alla radice dell’albero.
• Esaminare singolarmente gli alberi costituiti da ciascuna sotto-cartella.
Otteniamo, così, una strategia interessante: cerchiamo nella cartella radice i file aventi il
nome che termina con l’estensione richiesta, poi li cerchiamo ricorsivamente in ogni
sotto-cartella figlia della radice.
Fase 2 Combinate le soluzioni dei casi più semplici per risolvere il problema originario
Ci viene semplicemente chiesto di visualizzare i nomi dei file che troviamo, per cui non
ci sono risultati da combinare.
Se ci fosse stato chiesto di costruire un vettore contenente tutti i file trovati, avremmo
inserito nel vettore, inizialmente vuoto, tutti i file trovati nella cartella radice, aggiungendo
poi i risultati relativi a ciascuna sotto-cartella.
}
}
Nel nostro caso la fase di semplificazione del problema consiste semplicemente nell’esame
di tutti i file e sotto-cartelle:
for (File child : children)
{
if (child.isDirectoryO)
Cerca ricorsivamente in child.
else
Se il nome di child termina con extension
Visualizza il nome.
}
Il file FileFinderDemo. java nella cartella worked_example_l del Capitolo 12 del pacchetto dei
file scaricabili per questo libro completa la soluzione.
Abbiamo usato un oggetto per ciascuna cartella, ma si potrebbe, in alternativa, usare
un metodo ricorsivo statico:
File FileFinder2.javd
import java.io.File;
/**
Visualizza tutti i file il cui nome termina con l'estensione prevista.
@param aFile un file o una cartella
@param extension un'estensione per file (come ".java")
*/
public static void find(File aFile, String extension)
{
if (aFile.isDirectoryO)
{
for (File child : aFile.listFiles())
{
find(child, extension);
}
}
else
{
String fileName = aFile.toStringO;
if (fileName.endsWith(extension) )
{
System.out.print In (fileName) ;
}
}
/**
Verifica se una sottostringa della frase è un palindromo.
@param start l'indice del primo carattere della sottostringa
@param end l'indice dell'ultimo carattere della sottostringa
@return true se la sottostringa è un palindromo
V
public static boolean isPalindrome(String text, int start, int end)
Questo metodo si dimostra essere di più facile realizzazione della verifica originaria. Nelle
invocazioni ricorsive, modifichiamo semplicemente i parametri start e end in modo che
non vengano prese in esame le coppie di lettere uguali e i caratteri che non sono lettere:
non c’è più bisogno di costruire nuovi oggetti di tipo String per rappresentare le stringhe
che diventano sempre più brevi.
642 C a p it o l o 12
Notate che questo metodo non è ricorsivo: il metodo isPalindrome(String) invoca il metodo
ausiliario, isPalidrome(String, int, int). In questo esempio usiamo il s o v r a c c a r i c o per
dichiarare due metodi aventi lo stesso nome: il metodo isPalindrome con un parametro
di tipo String è quello destinato al pubblico utilizzo, mentre il secondo metodo, con un
parametro di tipo String e due parametri di tipo int, è il metodo ausiliario ricorsivo. Se
volete, potete evitare di usare metodi sovraccarichi, scegliendo per il metodo ausiliario
un nome diverso, come, ad esempio, substringIsPalindrome.
Usate la tecnica dei metodi ausiliari ricorsivi ogniqualvolta sia più semplice risolvere
un problema ricorsivo leggermente diverso dal problema originale.
R ic o r s io n e 643
Auto-valutazione
6. Dovevamo necessariamente dare lo stesso nome ai due metodi che abbiamo chiamato
isPalindrome?
7. In quale momento il metodo ricorsivo isPalindrome smette di invocare se stesso?
8. Per calcolare la somma dei valori presenti in un array, aggiungete il primo valore alla
somma dei valori rimanenti, eseguendo il calcolo ricorsivamente. Progettate un metodo
ausiliario ricorsivo per risolvere questo problema.
9. Come si può scrivere un metodo ricorsivo public static void sum(int[] a) senza usare
un metodo ausiliario? Perché è meno efficiente?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E12.6,E12.9 e E12.13,al termine del capitolo.
/. = 1
/2 = 1
Sn fr t-\ ftt-2
In sostanza, ciascun valore della sequenza è la somma dei due valori precedenti. I primi
dieci valori della sequenza sono, quindi:
1 ,1 ,2 ,3 ,5 ,8 ,13,21,34,55
File RecursiveFib.java
import java.util.Scanner;
/**
Questo programma calcola i numeri di Fibonacci usando un metodo ricorsivo.
*/ '
public class RecursiveFib
{
public static void main(String[] args)
644 C a p it o l o 12
Enter n: 50
fib(i) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(50) = 12586269025
File RecursiveFibTracer.java
import java.util.Scanner;
long f = fib(n);
Enter n: 6
Entering fib: n == 6
Entering fib: n == 5
Entering fib: n == 4
Entering fib: n == 3
Entering fib: n == 2
Exiting fib: n = 2 return value = I
Entering fib: n == I
Exiting fib: n = 1 return value = I
Exiting fib: n = 3 return value = 2
Entering fib: n == 2
Exiting fib: n = 2 return value = I
Exiting fib: n = 4 return value = 3
Entering fib: n == 3
Entering fib: n == 2
Exiting fib: n = 2 return value = I
Entering fib: n == I
Exiting fib: n = I return value = I
Exiting fib: n = 3 return value = 2
Exiting fib: n = 5 return value = 5
Entering fib: n == 4
646 C a p it o l o 12
Entering fib: n = 3
Entering fib: n = 2
Exiting fib: n = 2 return value = I
Entering fib: n = I
Exiting fib: n = I return value = I
Exiting fib: n = 3 return value = 2
Entering fib: n = 2
Exiting fib: n = 2 return value = I
Exiting fib: n = 4 return value = 3
Exiting fib: n = 6 return value = 8
fib(6) = 8
Figura 2 fib(6)
/ \
fib(3) fib(2) fib(2)
/ \ fib(l) fib(2)
/ \ fib(l)
/ \
fib(2) fib(l)
La Figura 2 mostra lo schema delle invocazioni usate per il calcolo di fib(6), A questo
punto risulta evidente cosa renda così lento questo metodo: gli stessi valori vengono
calcolati più e più volte. Ad esempio, il calcolo di fib(6) richiede di calcolare due volte
fib(4) e tre volte fib(3). Ciò è molto diverso dai calcoli che faremmo con carta e penna:
ci annoteremmo i valori man mano che li calcoliamo, sommando gli ultimi due per
generare il successivo fino al raggiungimento del valore desiderato, e nessun valore della
sequenza verrebbe calcolato due volte.
Imitando il procedimento “carta e penna”, otteniamo il programma seguente.
File LoopFib.java
import java.util.Scanner;
/**
Questo programma calcola i numeri di Fibonacci con un metodo iterativo.
*/
public class LoopFib
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.printC Enter n: ");
int n = in.nextIntO;
long f = fib(i);
System, out. print In ("fib (" + i + ") = " + f);
}
}
fib(50) = 12586269025
Questo metodo viene eseguito molto più velocemente della versione ricorsiva.
In questo esempio relativo al metodo fib la soluzione ricorsiva è più facile da
realizzare perché segue fedelmente la definizione matematica, ma viene eseguita molto
più lentamente della soluzione iterativa, perché calcola più volte molti risultati intermedi.
È sempre possibile velocizzare una soluzione ricorsiva trasformandola in un ciclo?
A volte una soluzione ricorsiva viene
Spesso le soluzioni iterativa e ricorsiva hanno sostanzialmente le stesse prestazioni. Ad
eseguita molto più lentamente
di una soluzione iterativa
esempio, ecco una soluzione iterativa per la verifica di un palindromo.
del medesimo problema, ma nella
public static boolean isPalindrome(String text)
maggior parte dei casi la soluzione
ricorsiva è soltanto poco più lenta.
{
int start = 0;
648 C a p it o l o 12
Questa soluzione usa due variabili con funzione di indice: start e end. 11primo indice parte
dalla posizione iniziale della stringa e avanza quando una lettera trova una corrispondenza
oppure quando viene ignorato un carattere che non è una lettera. Il secondo indice parte
dalla posizione finale della stringa e procede a ritroso. Quando le due variabili indice si
incontrano, il ciclo termina.
L’iterazione e la ricorsione vengono eseguite pressappoco alla stessa velocità. Se un
palindromo ha n caratteri, l’iterazione esegue il ciclo un numero di volte compreso tra n/2
e «, in relazione a quanti caratteri sono lettere, perché a ogni passo vengono modificate
entrambe le variabili indice oppure soltanto una. Similmente, la soluzione ricorsiva invoca
se stessa un numero di volte compreso tra n/2 e ^2, perché a ogni passo vengono eliminati
uno O due caratteri.
In tale situazione, la soluzione iterativa tende a essere un po’ più veloce, perché
ciascuna invocazione di un metodo ricorsivo richiede una certa quantità di tempo di
elaborazione del processore. In linea di principio, per un compilatore efficiente è possibile
eliminare l’esecuzione di invocazioni ricorsive nel caso in cui queste seguano uno schema
semplice, ma la maggior parte dei compilatori non fa questo. Da questo punto di vista,
una soluzione iterativa è quindi preferibile.
Ci sono, però, molti problemi che sono assai più facili da capire e risolvere
In molti casi una soluzione ricorsiva
è più facile da capire e da realizzare
ricorsivamente di quanto non lo siano iterativamente. A volte, come vedrete nell’esempio
correttamente, rispetto a
presentato nel prossimo paragrafo, non è per niente banale trovare una soluzione iterativa.
una soluzione iterativa. Nelle soluzioni ricorsive c’è una certa eleganza ed economia di pensiero che le rende
più attraenti. Come afferma lo scienziato dell’informazione L. Peter Deutsch (creatore
dell’interprete Ghostscript per il linguaggio di descrizione grafico PostScript):“Iterare è
umano, usare la ricorsione è divino”.
R ic o r s io n e 649
Auto-vaiutazione
10. È più veloce calcolare i numeri triangolari ricorsivamente, come visto nel Paragrafo 12.1,
oppure usando un ciclo che calcoli l + 2 + 3 + ... + width?
11. La funzione fattoriale può essere calcolata con un ciclo, seguendo la definizione n\ = \
X 2 X ... X n, oppure ricorsivamente, seguendo la definizione secondo cui 0! = 1 e «! =
{n - 1)! X n. In questo caso l’approccio ricorsivo è inefficiente?
12. Per calcolare la somma dei valori presenti in un array si può suddividere !’array in due
parti di dimensioni il più possibile simili (si parla impropriamente di “divisione a metà”),
calcolando ricorsivamente le somme relative alle due parti e sommando i risultati.
Confrontate le prestazioni di questo algoritmo con quelle di un ciclo che somma i valori.
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R12.7, R12.9, E12.7 e E12.27, al termine
del capitolo.
12.4 PemuLtazìonì
In questo paragrafo vedremo un esempio di ricorsione più complessa, che sarebbe difficile
Le permutazioni di una stringa
si possono ottenere in modo
realizzare con un semplice ciclo (come mostra l’Esercizio P 12.4, è possibile evitare l’uso
più naturale tramite la ritorsione
della ricorsione, ma la soluzione risultante è abbastanza complicata e non è più veloce).
piuttosto che usando un ciclo. Progetteremo una classe che elenchi tutte le permutazioni di una stringa (una
permutazione è semplicemente una qualsiasi disposizione delle lettere della stringa). Ad
esempio, la stringa "eat" ha sei permutazioni (compresa la stringa stessa).
"e a t"
"e tà "
"a e t"
"a te "
"te a "
"ta e "
"eat"
"eta"
"et"
"te"
"aet"
"ate"
File Permutations.java
import Java.util.ArrayList;
/**
Questo programma calcola le permutazioni di una stringa.
*/
public class Permutations
{
public static void main(String[] args)
{
for (String s : permutations("eat"))
{
System.out.println(s);
}
R ic o r s io n e 651
À Computer e societàJZJ,
A A A
I limiti del calcolo automatico p resa, d o v e te sa p e re c h e gli in f o r sposta la te stin a di u n o sp azio v erso
Vi siete m ai chiesti c o m e fa il vo stro m a tic i te o ric i h a n n o d im o s tra to c h e d estra o v erso sin istra, e p ro se g u i c o n
d o c e n te a essere sicuro c h e i v o stri è im p o ssib ile sv ilu p p a re q u e s to p r o l’is tru z io n e n u m e ro n \ E m o lto in
e s e rc iz i di p r o g r a m m a z io n e sia n o g ra m m a , indipendentemente da quanto te re ssa n te sa p ere c h e c o n q u e ste sole
corretti? M o lto p ro b a b ilm e n te guarda duramente ci prosiate. istru z io n i p o te te scriv ere p ro g ra m m i
la vostra so lu z io n e e forse la esegue E sisto n o diversi p ro b le m i irriso l a ltre tta n to p o te n ti q u a n to p o te te fare
c o n alcuni v alori di ingresso di p ro v ibili c o m e q u esto . 11 p rim o , c h ia m a in Java, a n c h e se sa re b b e in c re d ib il
va, m a so litam en te ha u n a so lu z io n e to problema della terminazione {halting m e n te n o io s o farlo. A gli in fo rm a tic i
c o rre tta c o n cu i c o n fro n ta re la vostra. problem), fu s c o p e rto dal ric e rc a to re te o ric i p ia c c io n o le m a c c h in e di T u
C iò suggerisce c h e ci p o tre b b e essere b r it a n n i c o A la n T u r i n g n e l 1 9 3 6 . rin g p e rc h é p o sso n o essere d e s c ritte
u n m e to d o m igliore: forse si p o tre b b e P o ic h é la sua ric e rc a era p r e c e d e n te sen za usare n ie n te di p iù d elle le g g i
fo rn ire il vostro p ro g ra m m a e il su o alla c o s tru z io n e del p r im o c a lc o la m a te m a tic h e .
p ro g ra m m a c o rre tto a un programma to re re a le ,T u rin g d o v e tte id e are u n a E sp resso in te r m in i di Jav a, il
che li confronti, u n p r o g ra m m a p e r m a c c h in a te o ric a , la macchina di p ro b le m a d ella te rm in a z io n e v ie n e
c o m p u te r c h e a n a liz z i e n tr a m b i i Turing, p e r sp ie g are c o m e i c o m co sì e n u n c ia to : “ E im p o ssib ile sc ri
p ro g ra m m i e c h e d e te r m in i se essi p u te r a v re b b e ro p o tu to fu n z io n a re . v ere u n p ro g ra m m a c o n d u e d ati di
ca lc o lan o gli stessi risu lta ti. O v v ia La m a c c h in a di T u rin g è c o s titu ita in g resso , il c o d ic e di u n a rb itra rio
m e n te , alla vostra so lu z io n e n o n si da u n lu n g o n astro m a g n e tic o , u n a p ro g ra m m a Java P e u n a strin g a /,
c h ie d e di essere id e n tic a a qu ella c h e te s tin a d i le ttu r a e s c rittu r a , e u n c h e d e te r m in i se il p ro g ra m m a P,
si sa essere co rretta: ciò c h e im p o rta è p ro g ra m m a c o n istru z io n i n u m e ra te , e se g u ito c o n I c o m e d a to di ingresso,
c h e esse p ro d u c a n o gli stessi risultati d el tip o : “ Se il sim b o lo a ttu a lm e n te te rm in e rà senza en tra re in u n ciclo
q u a n d o v e n g o n o fo rn iti lo ro gli stessi s o tto la te stin a è x , so stitu isc ilo c o n y. in fin ito ” . O v v ia m e n te , p e r alcu n i tipi
dati in ingresso. La macchina di Turing di p ro g ra m m i e di dati in ingresso è
C o m e p o tre b b e fu n z io n a re tale
Programma
p r o g ra m m a c o m p a r a to r e ? B e n e , il
E poi prosegui
c o m p ila to re Java sa c o m e leg g ere u n N u m ero di Se il sim bolo Sostituisci Poi sposta la
con l’istruzione
p ro g ra m m a e dare u n senso a classi, istruzione sul nastro è con testina verso
num ero
m e to d i ed en u n c ia ti, p e r cu i sem b ra
1 0 2 destra 2
p lau sib ile c h e q u a lc u n o possa, c o n 1 1 sinistra 4
q u alch e sforzo, scrivere u n p ro g ra m 2 0 0 destra 2
m a c h e leg g e d u e p ro g ra m m i Java, 1 1 destra 2
analizza ciò ch e fan n o e d e te rm in a 2 0 sinistra 3
se so n o in g rad o di riso lv ere lo stesso 3 0 0 sinistra 3
1 1 sinistra 3
p ro b le m a . E v id e n te m e n te , u n ta le
2 2 destra 1
p ro g ra m m a sarebbe m o lto in teressan
4 1 1 destra 5
te p e r i d o c e n ti, p e rc h é re n d e re b b e 2 0 sinistra 4
a u to m a tic a la c o rre z io n e delle prove
di p ro g ra m m a z io n e . Q u in d i, n o n o
stante tale p ro g ra m m a oggi n o n esista,
Unità tli controllo
p o tre m m o essere te n ta ti di provare a
svilupparlo, p e r v e n d e rlo alle u n iv e r
Testina Icttura/scrittura
sità di tu tto il m o n d o .
T u tta v ia , p r im a c h e in iz ia te a
N astro
ra c c o g lie re c a p ita li p e r q u e sta im
R ic o r s io n e 653
p e r ò , l ’e la b o r a z i o n e d e i d a ti p iù s e m p l i c i c r e a , a su a v o lt a , d a ti d a e la b o r a r e a n c o r a p iù
s e m p l i c i , la c u i e la b o r a z i o n e c r e a d a ti a n c o r a p iù s e m p l i c i , e c o s ì v ia , f i n c h é i d a ti c h e
d e v o n o e sse r e e la b o r a ti s o n o c o s ì s e m p lic i c h e si p o s s o n o c a lc o la r e i r is u lta ti s e n z a u lt e r io r e
a iu t o . P e n sa r e al p r o c e d i m e n t o è in t e r e s s a n t e , m a p u ò a n c h e c o n f o n d e r e le i d e e . C i ò c h e
im p o r t a è c o n c e n t r a r s i su l l iv e l lo g iu s t o : c o s t r u ir e la s o l u z i o n e a p a r tire d a u n p r o b le m a u n
p o ’ p iù s e m p l i c e , i g n o r a n d o il fa tto c h e v e n g a u sa ta la r ic o r s io n e p e r o t t e n e r e ta le r is u lta to .
.Autimlu!d2ÌQD£
13 . Q u a li s o n o le p e r m u t a z io n i d ella p arola d i q u a ttro le tte r e beat?
14 . La r ic o r s io n e c h e a b b ia m o p r o g e tta to p e r g e n e ra r e le p e r m u ta z io n i si fe rm a q u a n d o d e v e
elab orare u n a strin ga v u o ta . C o n q u a le s e m p lic e m o d ific a la r ic o r s io n e si in te r r o m p e r e b b e
e la b o r a n d o u n a str in g a d i lu n g h e z z a G o l ?
1 5 . P e r ch é n o n è fa c ile sv ilu p p a re u n a s o lu z io n e iterativa p e r la g e n e r a z io n e di p e r m u ta z io n i?
J L5„ RicorsioneMiLtua.
N e g l i e s e m p i p r e c e d e n t i u n m e t o d o in v o c a v a se s te s s o p e r r is o lv e r e u n p r o b le m a p iù
Una ricorsione mutua è caratterizzata
s e m p l i c e . A v o l t e u n i n s i e m e d i m e t o d i c o o p e r a n t i si i n v o c a n o l ’u n l ’a ltr o in m o d o
da un insieme di metodi cooperanti
r ic o r s iv o : in q u e s t o p a r a g r a fo e s a m i n e r e m o u n a tip ic a s i t u a z i o n e in c u i si r e a liz z a ta le
che si invocano l'un l'altro
ripetutamente. ricorsione mutua, u n a t e c n ic a s i g n i f i c a t i v a m e n t e p iù a v a n z a ta d e lla r i c o r s i o n e s e m p l i c e
d i c u i a b b ia m o p a r la to n e i p a r a g r a fi p r e c e d e n t i .
S v i l u p p e r e m o u n p r o g r a m m a c h e è in g r a d o d i c a lc o la r e i v a lo r i d i e s p r e s s io n i
a r it m e t ic h e , c o m e :
3+4*5
(3+4)*5
1-(2-(3-(4-5)))
Il c a l c o l o d i u n ’e s p r e s s io n e d i q u e s t o t i p o è c o m p l i c a t o d a l f a t t o c h e le o p e r a z i o n i * e /
h a n n o u n a p r e c e d e n z a p iù e le v a ta d e lle o p e r a z i o n i + e in o lt r e , si p o s s o n o u sa re p a r e n te s i
p e r r a g g r u p p a r e s o t t o - e s p r e s s io n i.
La F ig u r a 3 m o s tr a u n i n s i e m e d i diagrammi sintattici c h e d e s c r iv e la sin ta ssi d i
q u e s t e e s p r e s s io n i. P e r c a p ir e c o m e f u n z i o n a n o d ia g r a m m i d i q u e s t o t ip o , e s a m i n i a m o
l ’e s p r e s s io n e 3+4*5.
• E n t r i a m o n e l d ia g r a m m a s i n t a t t i c o c o r r i s p o n d e n t e a u n a e s p r e s s i o n e (expression): la
fr e c c ia p u n t a d ir e t t a m e n t e a u n t e r m i n e (term), p e r c u i n o n a b b ia m o a lte r n a tiv e .
• E n t r i a m o n e l d i a g r a m m a s i n t a t t i c o c o r r i s p o n d e n t e a u n t e r m i n e : la f r e c c i a p u n t a
d ir e t t a m e n t e a u n f a t t o r e (factor), la s c ia n d o c i n u o v a m e n t e s e n z a a lt e r n a t iv e .
• E n t r i a m o n e l d ia g r a m m a c h e d e s c r iv e u n f a t t o r e e , q u e s ta v o lt a , s ia m o d i f r o n t e a u n a
s c e lt a tra d u e a lt e r n a t iv e : s e g u ir e il r a m o s u p e r i o r e o il r a m o i n f e r i o r e . D a t o c h e il
p r i m o e l e m e n t o in in g r e s s o è il n u m e r o 3 e n o n u n a p a r e n te s i to n d a a p e r ta , d o b b i a m o
s e g u ir e il r a m o in f e r io r e .
R ic o r s io n e 655
Figura 3
Diagram m i sintattici
per la valutazione
di un'espressione
< IX expression X IH
factor ■
num ber
• A c c e t t i a m o il d a to in in g r e s s o p e r c h é c o r r is p o n d e alla d e f i n i z i o n e d i n u m e r o (number).
A q u e s t o p u n t o i d a ti in in g r e s s o a n c o r a da e la b o r a r e s o n o c o s t i t u i t i d alla s tr in g a +4*5.
• S e g u i a m o la f r e c c i a u s c e n t e d a n u m e r o f i n o a lla f i n e d ì f a t t o r e : p r o p r i o c o m e
a v v i e n e n e l l ’i n v o c a z i o n e d i u n m e t o d o , t o r n i a m o v e r s o l ’a lto , r it r o v a n d o c i alla f in e
d e l l ’e l e m e n t o f a t t o r e p r e s e n t e n e l d ia g r a m m a c h e d e s c r iv e u n t e r m i n e .
• A q u e s t o p u n t o d o b b i a m o n u o v a m e n t e s c e g lie r e : to r n a r e in d ie t r o f a c e n d o u n c ic l o
a l l ’i n t e r n o d e l d i a g r a m m a c h e d e s c r i v e u n t e r m i n e o p p u r e u s c i r e d a e s s o . Il d a t o
s u c c e s s iv o in in g r e s s o è il s e g n o +, c h e n o n c o r r is p o n d e n é al s i m b o l o * n é al s i m b o l o /
c h e sa r e b b e r o n e c e s s a r i p e r e n tr a re n e l c ic lo , p e r c u i u s c ia m o e t o r n i a m o 2d V e s p r e s s i o n e .
• D i n u o v o , p o s s i a m o e n tr a r e in u n c i c l o c h e c i r ip o r t a a ll’i n d ie t r o o p p u r e u s c ir e d a l
d ia g r a m m a , m a in q u e s t o c a s o il s e g n o + c o r r i s p o n d e ad u n a d e l le d u e s c e lt e c h e c i
p o r t a n o a c o m p i e r e u n c ic lo : a c c e t t ia m o ta le d a to in in g r e s s o e t o r n i a m o a ll’e l e m e n t o
t e r m i n e . I d a ti in i n g r e s s o a n c o r a da e la b o r a r e s o n o c o s t i t u i t i d a lla s tr in g a 4*5.
P r o c e d e n d o in q u e s t o m o d o u n ’e s p r e s s io n e v i e n e s c o m p o s t a in u n a s e q u e n z a d i t e r m i n i ,
se p a r a ti d a i s e g n i + o - , e c ia s c u n t e r m i n e v i e n e a su a v o lt a s c o m p o s t o in u n a s e q u e n z a
d i f a t t o r i, s e p a r a ti d a s e g n i * o /; c ia s c u n f a tto r e , i n f i n e , è u n n u m e r o o u n ’e s p r e s s io n e
r a c c h iu s a fra p a r e n te s i t o n d e . Q u e s t a s c o m p o s i z i o n e p u ò e sse r e r a p p r e se n ta ta m e d ia n t e u n
a lb e r o e la F ig u r a 4 m o s tr a c o m e v e n g o n o d e r iv a t e d al d ia g r a m m a s in t a t t ic o le e s p r e s s io n i
3+4*5 e (3+4)*5.
656 C a p it o l o 12
Figura 4 Expression
Alberi sintattici
per due espressioni i
Term
Factor Factor
Expression
i
Num ber
Term Term
I
Factor Factor Factor
’actc
i
Num ber Num ber Num ber
V V V V V V
3 + 4 * 5 5
{
tokenizer.nextTokenO; // ignora "+" o "-"
int value2 = getTermValue();
if ('■+".equals(next)) { value = value + value2; }
else { value = value - value2; }
}
else
{
done = true;
}
}
return value;
}
P er e v id e n z ia r e c o n c h i a r e z z a la r i c o r s i o n e m u t u a , s e g u i a m o p a s s o d o p o p a s s o la
v a l u t a z io n e d e l l ’e s p r e s s io n e (3+4)*5:
Come sempre accade con le soluzioni ricorsive, dobbiamo assicurarci che la ricorsione
abbia termine. In questa situazione ciò si vede facilmente, perché, quando il metodo
getExpressionValue invoca se stesso, la seconda invocazione elabora una sotto-espressione
più corta dell’espressione originale. Dato che ogni invocazione ricorsiva estrae dai dati
di ingresso alcuni elementi, la ricorsione deve necessariamente terminare.
File Evaluator.javd
/**
Costruisce un valutatore.
@param anExpression una stringa che contiene
l'espressione da valutare
*/
public Evaluator(String anExpression)
{
tokenizer = new ExpressionTokenizer(anExpression);
}
/**
Valuta l'espressione.
@return il valore dell^espressione
*/
public int getExpressionValueO
{
int value = getTermValue();
boolean done = false;
while (!done)
{
String next = tokenizer.peekToken();
if ("+".equals(next) || "-".equals(next))
{
tokenizer.nextTokenO; // ignora "+" o "-"
int value2 = getTermValue();
if ("+".equals(next)) { value = value + value2; }
else { value = value - value2; }
}
else
{
done = true;
}
}
return value;
}
/**
Valuta il successivo termine nell'espressione,
^return il valore del termine
*/
R ic o r s io n e 659
File ExpressionTokenizer.java
11 /**
12 Costruisce uno scompositore (tokenizer).
13 @param aninput la stringa da scomporre
14 */
15 public ExpressionTokenizer(String aninput)
16 {
17 input = aninput;
18 start = 0;
19 end = 0;
20 nextTokenO; // cerca il primo token
21 }
22
23 /*
24 Restituisce il token successivo senza estrarlo.
25 @return il token successivo (nuli se non ce ne sono più)
26 */
27 public String peekToken()
28 {
29 if (start >= input.lengthO) { return nuli; }
30 else { return input.substring(start, end); }
31 }
32
33 /**
34 Restituisce il token successivo e si sposta al token seguente.
35 @return il token successivo (nuli se non ce ne sono più)
36 */
37 public String nextToken()
38 {
39 String r = peekToken();
40 start = end;
41 if (start >= input.lengthO) { return r; }
42 if (Character.isDigit(input.charAt(start)))
43 {
44 end = start + l;
45 while (end < input.length()
46 && Character.isDigit(input.charAt(end)))
47 {
48 end++;
49 }
50 }
51 else
52 {
53 end = start + l;
54 }
55 return r;
56 }
tej}
File ExpressionCakulator.java
import java.util.Scanner;
/**
Questo programma calcola il valore di un'espressione
costituita da numeri, operatori aritmetici e parentesi tonde.
*/
R ic o r s io n e 661
Auto-jtaiulazifiiifi
16. Q u a l è la d iffe r e n z a tra t e r m in e e fa ttore? P e r c h é ci s e r v o n o e n tr a m b i q u e s ti c o n c e tti?
17. P e r c h é il v a lu ta to r e d i e sp r e ssio n i usa la r ic o r s io n e m u tu a ?
1 8 . C o s a s u c c e d e se c e r c a te d i va lu ta re l ’e s p r e s s io n e 3+4*)5, c h e n o n è valid a? In p a r tico la re ,
q u a le m e t o d o la n cia u n ’e c c e z io n e ?
Perfarpratica
A q u e s to p u n t o si c o n s ig lia d i sv o lg e r e g li e se r c iz i R 1 2 . 1 3 e E 1 2 .2 1 , al te r m in e d e l c a p ito lo .
12.6 Backtracking
Il b a c k t r a c k i n g ( l e t t e r a lm e n t e , “ t o r n a r e su i p r o p r i p a s s i” ) è u n a t e c n ic a d i s o l u z i o n e d e i
La tecnica di backtracking esamina
p r o b l e m i c h e c o s t r u i s c e s o l u z i o n i p a r z ia li v ia v ia p iù v i c i n e alla s o l u z i o n e fin a le . S e u n a
soluzioni parziali, abbandonando
s o l u z i o n e p a r z ia le n o n p u ò e s s e r e resa c o m p l e t a , la si a b b a n d o n a e si r it o r n a a e s a m in a r e
quelle che non porteranno a nulla
e tornando sui propri passi per a ltre s o l u z i o n i c a n d id a te .
1. u n a p r o c e d u r a c h e e s a m in i u n a s o l u z i o n e p a r z ia le e d e t e r m i n i se:
• a c c e tta r la c o m e e f fe t t iv a s o l u z i o n e d e l p r o b le m a , o p p u r e :
• a b b a n d o n a r la ( p e r c h é v i o la a l c u n e r e g o l e o p p u r e p e r c h é è e v i d e n t e c h e n o n p u ò
p o r ta r e a u n a s o l u z i o n e v a lid a ), o p p u r e :
• c o n t i n u a r e a e s t e n d e r la v e r s o u n a s o l u z i o n e c o m p l e t a ;
2. u n a p r o c e d u r a c h e e s t e n d a u n a s o l u z i o n e p a r z ia le , g e n e r a n d o u n a o p iù s o l u z i o n i
c h e s ia n o p iù v i c i n e a ll’o b i e t t iv o .
Q u i n d i , la t e c n ic a d i b a c k t r a c k in g si p u ò im p l e m e n t a r e m e d i a n t e il s e g u e n t e a l g o r i t m o
r ic o r s iv o :
662 C a p it o l o 12
R\so\V\(soluzioneParziale):
isòmm(soluzioneParziale)
Se accettata
Aggiungi soluzioneParziale alla lista delle soluzioni.
Altrimenti se non abbandonata
Per ogni p in is{ew&\(soluzioneParziale)
Risolvila).
O v v i a m e n t e le p r o c e d u r e n e c e s s a r ie p e r e s a m in a r e e d e s t e n d e r e u n a s o l u z i o n e p a r z ia le
d i p e n d o n o d a lla n a tu r a d e l p r o b le m a .
C o m e e s e m p i o p r o g e t t e r e m o u n p r o g r a m m a c h e tr o v i t u t t e le s o l u z i o n i d e l p r o b le m a
d e l le o t t o r e g in e : p o s iz i o n a r e o t t o r e g i n e su u n a s c a c c h ie r a in m o d o c h e n e s s u n a d i e s s e
p o s s a a t t a c c a r n e u n ’altra s e c o n d o le r e g o l e d e g l i s c a c c h i. In altre p a r o le , n o n c i d e v o n o
e s s e r e d u e r e g i n e su lla stessa r ig a , c o l o n n a o d ia g o n a le . La F ig u r a 5 m o s tr a u n a p o s s ib ile
s o lu z io n e .
In q u e s t o p r o b le m a è f a c ile e s a m in a r e u n a s o l u z i o n e p a r z ia le : se d u e r e g in e si a tta c c a n o
r e c ip r o c a m e n t e , la s o l u z i o n e v a a b b a n d o n a ta ; a l t r im e n t i, se h a o t t o r e g in e , v a a c c e tta ta ;
a l t r i m e n t i, va e s te s a .
L’e s t e n s i o n e d i u n a s o l u z i o n e p a r z ia le è a l t r e t t a n t o s e m p l i c e : b a s ta a g g i u n g e r e
u n ’u l t e r i o r e r e g in a in u n a c a s e lla v u o t a .
P e r o t t e n e r e u n a m i g li o r e e f f i c ie n z a , r e n d e r e m o u n p o ’ p iù s is t e m a t ic a la p r o c e d u r a
d i e s t e n s i o n e : p o s i z i o n e r e m o la p r im a r e g in a n e lla r ig a 1, la s e c o n d a r e g in a n e lla r ig a 2
e c o s ì v ia .
P r o g e t t ia m o la c la s se PartialSolution c h e r a p p r e s e n ta u n a s o l u z i o n e p a r z ia le , c o n
a l c u n e r e g i n e p o s iz i o n a t e , e c h e h a m e t o d i p e r e s a m in a r e e p e r e s t e n d e r e la s o l u z i o n e .
Figura 5
Una soluzione
del problem a
delle otto regine ^
d e f g h
R ic o r s io n e 663
Il metodo examine ha il semplice compito di verificare se, in una soluzione parziale, due
regine si attaccano reciprocamente:
11 metodo extend riceve una soluzione parziale e ne fa otto copie, aggiungendo a ciascuna
una nuova regina in una diversa colonna:
La classe Queen si trova al termine di questo paragrafo. L’unico problema che rima
ne da risolvere è determinare se due regine si attaccano reciprocamente in diagonale, e
si può fare facilmente in questo modo: si calcola la pendenza della retta che le congiun
ge e si verifica se è uguale a ± l.T ale condizione può essere semplificata in questo
modo:
(D
4% ^
■ « ■ ■ W i i n i i « ■ I W D □ W Q ■ W B ^
File PartialSolution.java
import java.util.Arrays;
/**
Esamina una soluzione parziale.
@return un valore tra ACCEPT, ABANDON e CONTINUE
*/
public int exam in eO
{
for (int i = 0; i < queens.length; i++)
{
for (int j = i + I; j < queens.length; j++)
{
if (queens[i].attacks(queens[j])) { return ABANDON; }
}
}
if (queens.length == NOUEENS) { return ACCEPT; }
else { return CONTINUE; }
}
File Queen.java
/**
Verifica se questa regina ne attacca un'altra.
@param other l'altra regina
@return true se questa regina e l'altra si trovano sulla stessa
riga, colonna o diagonale.
*/
public boolean attacks(Oueen other)
{
return row == other.row
11 column == other.column
11 Math.abs(row - other.row) Math.abs(column - other.column);
}
File EightQueen.java
/**
Visualizza tutte le soluzioni del problema che si possono
ottenere estendendo una soluzione parziale data.
@param sol la soluzione parziale
*/
public static void solve(PartialSolution sol)
{
int exam = sol.examine();
if (exam == PartialSolution.ACCEPT)
{
System.ou t .printIn(sol);
}
else if (exam == PartialSolution.CONTINUE)
{
for (PartialSolution p : sol.extend())
{
solve(p);
}
}
}
}
(92 soluzioni)
.JutO-MitazimiS
19. P e r c h é n e l m e t o d o examine il p r im o v a lo re di j è i + l?
2 0 . C o n t in u a t e a s e g u ir e , p a sso d o p o p a sso, la s o l u z i o n e d e l p r o b le m a d e lle q u a ttr o r e g in e
in izia ta n e lla F igu ra 6 . Q u a n te s o lu z io n i e s is t o n o c o n la p r im a r e g in a n e lla c o lo n n a a?
2 1 . Q u a n te s o lu z io n i ha c o m p le s s iv a m e n t e il p r o b le m a d e lle q u a ttro r eg in e ?
668 C a p it o l o 12
Perfarpratica
A q u e s to p u n to si co n sig lia d i sv o lg ere gli esercizi E 1 2 .2 2 , E 1 2 .2 5 e E 1 2 .2 6 , al te rm in e
d el c a p ito lo .
Figura 7
LeTorri di Hanoi
FileTowersOfHanoilnstructions.java
/**
Visualizza istruzioni per spostare una pila di dischi da un piolo a un altro.
@param disks il numero di dischi da spostare
@param from il piolo da cui spostare i dischi
@param to il piolo verso cui spostare i dischi
*/
public static void move(int disks, int from, int to)
{
if (disks > 0)
{
int other = 6 - from - to;
move(disks - I, from, other);
System.out.printIn("Move disk from peg " + from + " to " + to);
move(disks - I, other, to);
}
Queste istruzioni possono essere sufficienti per i monaci, ma non è semplice capire
quale sia la strategia da seguire per risolvere il rompicapo. Cerchiamo di migliorare il
programma in modo che esegua effettivamente gli spostamenti e mostri il contenuto
delle torri dopo ogni mossa.
Usiamo la classe Tower per gestire i dischi presenti in una torre, cioè in un piolo. Ogni
disco è rappresentato da un numero intero che indica la sua dimensione, da 1 a che è
il numero complessivo di dischi presenti nel rompicapo.
P r o g e t t i a m o m e t o d i p e r e li m i n a r e il d i s c o c h e si tr o v a in c im a a lla t o r r e , p e r
a g g i u n g e r v i u n d i s c o in c im a e p e r m o s tr a r e il c o n t e n u t o d e lla t o r r e c o m e e l e n c o d i
d i m e n s i o n i d e i s u o i d i s c h i, a d e s e m p i o [ 5, 4 , 1 ].
FileTower.java
import java.util.ArrayList;
/**
Costruisce una torre con dischi di dimensione decrescente.
@param ndisks il numero di dischi
*/
public Tower(int ndisks)
{
disks = new ArrayList<Integer>();
for (int d = ndisks; d >= 1; d--) { disks.add(d); }
}
/*
Elimina da questa torre il disco che si trova in cima.
@return la dimensione del disco eliminato
*/
public int remove0
{
return disks.remove(disks.size() - l);
}
/*
R ic o r s io n e 671
} * * ’
Il suo metodo move esegue per prima cosa uno spostamento, poi visualizza il contenuto
delle torri:
public void move(int disks, int from, int to)
{
if (disks > o)
{
int other = 3 - from - to;
move(disks - I, from, other);
towers[t o ].add(towers[from].remove());
System.ou t.println(Arrays.toString(towers));
move(disks - I, other, to);
}
}
In questo programma abbiamo identificato i pioli con i numeri 0,1 e 2, per cui l’indice
del piolo other è dato da 3 - from - to.
Ecco il metodo main:
public static void main(String[] args)
{
final int NDISKS = 5;
TowersOfHanoi towers = new TowersOfHanoi(NDlSKS);
672 C a p it o l o 12
towers.move(NOISKS, 0, 2);
}
File TowersOfHanoiDemo.java
1 ] import Java.util.ArrayList;
2 I import java.util.Arrays;
3 I
4 :/ * *
R ic o r s io n e 673
File TowersOfHanoi.java
import java.util.Arrays;
Un esempio di ricorsione complessa che non si può risolvere con un semplice ciclo
• Le p e r m u ta zio n i di una stringa si p o s so n o o tten er e in m o d o p iù naturale tram ite la ric o rsio n e
p iu tto sto c h e u san d o u n ciclo .
Usare il backtracking per risolvere problemi che richiedono l'esplorazione di più percorsi
• La tecn ica di b ack track in g esam ina so lu z io n i parziali, ab b an d o n a n d o q u elle ch e n o n p orteran
n o a nulla e torn a n d o sui propri passi p er p rendere in esam e altri candidati alla so lu z io n e finale.
R 1 2 . 6 . S criv ete un a d e fin iz io n e ricorsiva di x ”, c o n « > 0 , c h e sia sim ile alla d e fin iz io n e ricorsiva
d ei n u m er i di F ib o n a cci. S u g g e r im e n to : c o m e si calcola a partire da C o m e si fa term in are
la rico rsio n e?
R 1 2 . 8 . S criv ete un a d e fin iz io n e ricorsiva di n\ = 1 X 2 X ... X « c h e sia sim ile alla d e fin iz io n e
ricorsiva d ei n u m er i di F ib o n a cci.
R l 2 . 9 . S c o p r ite q u an te v o lte la v e rsio n e ricorsiva di fib in voca se stessa. U sa te una variab ile statica,
fibCount, e in crem en ta tela d o p o o g n i in v o c a z io n e di fib. Q u a l è la rela zio n e tra fib(n) e fibCount?
R 1 2 . 1 2 . L’E sercizio P 1 2 .5 m ostra una strategia iterativa p er gen erare tu tte le p e r m u ta zio n i della
seq u en za (0, 1 1 ) . S p ieg a te per q u ale m o tiv o l ’a lg o r itm o p r o d u ce il risultato corretto.
Issrcizi di proatampiazione,
* E 1 2 . 1 . In una classe Rectangle dotata d e lle variabili di esem p lare width e height (risp ettivam en te,
largh ezza e altezza d el retta n g o lo ), d e fin ite il m e to d o rico rsiv o get Area: c o stru ite un r etta n g o lo la
cu i largh ezza sia in ferio re di u n ’u n ità risp etto all’o r ig in a le e in v o c a ten e il m e to d o get Area.
E 1 2 . 2 . In una classe Square dotata della variab ile di esem p lare width (d im e n sio n e del lato del
qu ad rato), d e fin ite il m e to d o rico rsiv o get Area: co stru ite un qu adrato il cu i lato sia in ferio re di
u n ’u n ità risp etto all’o r ig in a le e in v o c a ten e il m e to d o getArea.
^ E l 2 . 4 . S criv ete un m e to d o r ico rsiv o c h e costru isca una stringa c o n te n e n te le cifre b in arie di un
n u m er o in tero n . S e n è pari, allora l’ultim a cifra è 0; se n è dispari, l’ultim a cifra è 1. C a lc o la te
r ic o rsiv a m en te le altre cifre.
** E 1 2 . 8 . U sa te la r ic o rsio n e p er realizzare il m e to d o
676 C a p it o l o 12
E l 2 .9 . U sa te la r ic o r s io n e p er realizzare il m e to d o
c h e restitu isce la p o s iz io n e in izia le della prim a sotto strin g a di text c h e sia u gu ale alla stringa str.
R e s titu ite —1 se str n o n è una so tto strin g a di text. A d e se m p io , indexOf("Mississippi", "sip”)
restitu isce 6.
S u g g e r im e n to : q u esto è u n p o ’ p iù d ifficile d el p rob lem a p r e ce d e n te , p erch é d o v e te ten ere traccia di
q u a n to sia lon tan a d all’in iz io della frase la c o rr isp o n d e n z a c h e state cercan d o; in serite tale valore
c o m e param etro di u n m e to d o ausiliario.
rum , um ,
R ic o r s io n e 677
O sservate c h e n o n è n ecessa rio c h e i so tto in s ie m i sian o so tto strin g h e: ad ese m p io , "rm" n o n è una
so tto strin g a di "rum".
[[1], [7], [2], [9]], [[1, 7], [2], [9]], [[1], [7, 2], [9]], [[l, 7, 2], [9]],
[[1], [7], [2, 9]], [[1, 7], [2, 9]], [[1], [7, 2, 9]], [[l, 7, 2, 9]]
S u g g e r im e n to : p er prim a cosa g e n era te tutti i s o tto -v e tto r i del v etto re p rivato d e ll’u ltim o e le m e n to ,
c h e , p o i, p u ò essere a g g iu n to in fo n d o all’u ltim o s o tto -v e tto r e o p p u re p u ò costitu ire un s o tto
v etto re di lu n g h e zz a un itaria.
E 1 2 . 2 2 . L’a lg o r itm o di b ack tra ck in g fu n zio n a per qualsiasi p rob lem a le cu i s o lu z io n i parziali
p o ssa n o essere valutate ed estese. D e fin ite un tip o in terfaccia, PartialSolution, d o ta to d ei m e to d i
examine e extend; p o i, realizzate u n m e to d o , solve, c h e ela b o ri o g g e tti di tip o PartialSolution e una
classe, EightQueensPartialSolution, c h e im p le m e n ti l’in terfaccia.
Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
13
Ordinamento e ricerca
• S tu d ia re a lc u n i a lg o ritm i di o rd in a m e n to e d i ric e rc a
• O sse rv a re c o m e a lg o ritm i c h e ris o lv o n o lo stesso p ro b le m a p o ssa n o avere p re sta z io n i
m o lto d iv e rse
• C a p ire la n o ta z io n e O - g r a n d e
• Im p a ra re a stim are le p re sta z io n i di a lg o ritm i e a c o n fro n ta rle
• S criv ere c o d ic e p e r m is u ra re il te m p o d ’e s e c u z io n e d i u n p ro g ra m m a
[0][1][2][3][4]
11 9 17 5 12
[0][1][2][3][4]
|'5l9 |l7|ll|Ì2
[0][1][2][3][4]
11 12
[0][1][2][3][4]
|j5Ì9gl7|ll|l2
[0][1][2][3][4]
5|9Ì17|34^
O r d in a m e n t o e r ic e r c a 681
Q u e s to ci p o rta ad avere u n a z o n a n o n a n c o ra e la b o ra ta d i lu n g h e z z a 1, m a, n a tu ra lm e n te ,
u n a z o n a di lu n g h e z z a 1 è se m p re o rd in a ta . A b b ia m o fin ito .
P ro v ia m o a sc riv e re il c o d ic e p e r q u e s to a lg o ritm o , c h ia m a to ordinamento per
selezione {selection sort). P e r q u e s to p ro g ra m m a , c o m e p e r gli a ltri di q u e s to c a p ito lo ,
u tiliz z e re m o u n m e to d o a u silia rio p e r g e n e ra re u n array d i v a lo ri casuali: lo in s e ria m o
n ella classe ArrayUtil p e r n o n essere c o s tre tti a rip e te r lo in o g n i ese m p io . P e r visu alizzare
il c o n te n u to di u n array u sia m o in v e c e la strin g a re stitu ita dal m e to d o sta tic o toString
d ella classe java.util.Arrays (visto n e l P arag rafo 7 .3 .4 ). A g g iu n g ia m o alla classe ArrayUtil
a n c h e u n m e to d o , swap, c h e sc am b ia tra lo ro d u e e le m e n ti di u n array (nel P a rag rafo 7 .3 .8
a b b ia m o p re s e n ta to a lc u n i d e tta g li relativ i allo sc a m b io di e le m e n ti in u n array).
Q u e s to a lg o ritm o o rd in e rà u n array di n u m e ri in te ri. Se la v e lo c ità n o n fosse u n
a r g o m e n to di in teresse o se n o n esistessero m e to d i di o r d in a m e n to m ig lio ri, p o tr e m m o
in te r ro m p e re q u i la d isc u ssio n e s u lf o r d in a m e n to . T u tta v ia, c o m e m o stre rà il p ara g ra fo
successivo, q u e s to a lg o ritm o , p u r e sse n d o a s so lu ta m e n te c o r re tto , h a p re sta z io n i d av v e ro
d e lu d e n ti q u a n d o v ie n e e s e g u ito su g ra n d i in sie m i di d ati.
N e lla se z io n e A rg o m e n ti avanzati 1 3 .2 v ie n e p re se n ta to l’a lg o ritm o di o rd in a m e n to
p e r in s e rim e n to {insertion sort), u n altro a lg o ritm o di o r d in a m e n to m o lto se m p lic e.
File SelectionSorter.java
1
2 Il metodo sort di questa classe ordina un array
3 usando l'algoritmo di ordinamento per selezione.
4 */
5 public class SelectionSorter
6 {
7
8 Ordina un array usando l'ordinamento per selezione.
9 ^param a !'array da ordinare
10 */
11 public static void sort(int[] a)
12 {
13 for (int i = 0; i < a.length - l; i++)
14 {
15 int minPos = minimumPosition(a, i);
16 ArrayUtil.swap(a, minPos, i);
17 }
18 }
19
20
21 Trova l'elemento minimo in una zona terminale di un array.
22 @param a !'array da analizzare
23 @param from la prima posizione da analizzare in a
24 @return la posizione dell'elemento minimo nella porzione di array
25 a[from] . . . a[a.length - l]
26 */
27 private static int minimumPosition(int[] a, int from)
28 {
29 int minPos = from;
30 for (int i = from + 1; i < a.length; i++)
31 {
ll2j if (a[i] < a[minPos]) { minPos = i; }
682 C a p it o l o 13
}
return minPos;
File SelectionSortDemo.java
import java.util.Arrays;
Select ionSorter.sort(a);
System.out.println(Arrays.toString(a));
File ArrayUtil.java
import java.util.Random;
/**
Costruisce un array contenente numeri interi casuali.
@param length la lunghezza dell'array
@param n il numero di valori diversi possibili
^return un array contenente length numeri
casuali compresi fra O e n - I
*/
public static int[] randomIntArray(int length, int n)
{
int[] a = new int[length];
for (int i = 0; i < a.length; i++)
{
a[i] = generator.nextlnt(n);
}
return a;
/**
Scambia tra loro due elementi di un array.
@param a !'array
@param i la posizione del primo elemento da scambiare
@param j la posizione del secondo elemento da scambiare
*/
public static void swap(int[] a, int i, int j)
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
AuKmlutaziooe
1. P e rc h é nel m e to d o swap se rv e la v aria b ile temp? C o sa s u c c e d e re b b e se assegnassim o
se m p lic em e n te a[i] a a[j] e a[j] a a[i]?
2. Q u ali so n o i passi co m p iu ti dall’a lg o ritm o di o rd in a m e n to p e r selezione n ell’o rd in are la
sequenza 6 5 4 3 2 1?
3. C o m e si p u ò m o d ific a re l’a lg o ritm o di o rd in a m e n to p e r se le z io n e p e rc h é o rd in i gli
elem e n ti in o rd in e d ec resc en te (cioè co n l’e le m e n to m assim o all’inizio d e ll’array)?
4. Im m a g in ate di aver m o d ific ato l’a lg o ritm o di o rd in a m e n to p e r selezione in m o d o ch e
parta dalla fine d ell’array, p ro c e d e n d o verso la po sizio n e iniziale: ad ogni passo l’e lem e n to
ch e si trova nella p o sizio n e in esam e v ie n e scam biato co n il valore m in im o . C h e risultati
p ro d u c e qu esto a lg o ritm o m odificato?
Perfarpratica
A q u e s to p u n to si c o n sig lia di sv o lg e re gli esercizi R 1 3 .2 , R 1 3 .1 2 , E 1 3 .1 e E 1 3 .2 , al
te rm in e d el ca p ito lo .
13R PrestazionideirordinamigjjtQji^rselezione^
P er m isu ra re le p re sta z io n i te m p o ra li di u n p ro g ra m m a p o tre s te s e m p lic e m e n te ese g u irlo
e usare u n c ro n o m e tro p e r m isu ra re il te m p o trasc o rso , m a la m a g g io r p a rte d ei n o s tri
p ro g ra m m i v ie n e e seg u ita m o lto ra p id a m e n te e n o n sa re b b e facile m isu ra re i te m p i in
m o d o a c c u ra to in q u e s to m o d o . In o ltre , a n c h e q u a n d o l’e s e c u z io n e di u n p ro g ra m m a
ric h ie d e u n te m p o p e rc e p ib ile , u n a p a rte di q u e l te m p o v ie n e s e m p lic e m e n te usata p e r
c a ric a re il p ro g ra m m a dal d isco alla m e m o r ia e p e r v isu alizzare i risu lta ti sullo s c h e rm o
(cose p e r le q u ali n o n d o v re m m o p en a liz za rlo ).
P er m isu rare in m o d o p iù a c cu rato il te m p o di e sec u zio n e di u n a lg o ritm o p ro g e tte re m o
la classe StopWatch, c h e fu n z io n a p r o p rio c o m e u n v ero c ro n o m e tro : p o te te farlo p a rtire ,
fe rm a rlo e le g g e re il te m p o trasco rso . La classe usa il m e to d o System.currentTimeMillis,
684 C a p it o l o 13
FileStopWatch.java
/**
Un cronometro misura il tempo che trascorre mentre è in azione. Potete
avviare e arrestare ripetutamente il cronometro. Potete utilizzare un
cronometro per misurare il tempo di esecuzione di un programma.
*/
public class StopWatch
{
private long elapsedTime;
private long startTime;
private boolean isRunning;
/**
Ferma il cronometro. Il tempo trascorso dall'ultimo avvio del
cronometro viene sommato al tempo totale misurato.
*/
public void stopO
{
if (!isRunning) { return; }
isRunning = false;
long endTime = System.currentTimeMillis();
elapsedTime = elapsedTime + endTime - startTime;
}
/*
Restituisce il tempo totale misurato.
@return il tempo totale misurato
O r d in a m e n t o e r ic e r c a 685
/* *
Ferma il cronometro e azzera il tempo totale.
*/
public void resetO
{
elapsedTime = 0;
isRunning = false;
}
}
E d e c c o c o m e u t i l i z z e r e m o il c r o n o m e t r o p e r m is u r a r e le p r e s t a z io n i d e l F a l g o r it m o d i
o r d in a m e n to .
File SelectionSortTimer.java
import java.util.Scanner;
timer.start();
SelectionSorter.sort(a);
timer. stopO;
686 C a p it o l o 13
A v v ia n d o la r ile v a z io n e d e l t e m p o i m m e d ia t a m e n t e p r im a d e l l ’o r d i n a m e n t o e a r r e sta n d o la
Per misurare il tempo di esecuzione
s u b i t o d o p o si m is u r a il t e m p o r ic h i e s t o p e r o r d in a r e i d a ti, s e n z a t e n e r c o n t o d e l t e m p o
t u n metodo, chiedete l'ora al sistema
c h e o c c o r r e p e r le o p e r a z i o n i d i i n p u t e d i o u t p u t .
subito prima e subito dopo
!Invocazione del metodo stesso. La F ig u r a 1 m o s tr a i r is u lta ti d i a l c u n e e s e c u z i o n i d e l p r o g r a m m a . Q u e s t e r il e v a z i o n i
s o n o sta te o t t e n u t e c o n u n p r o c e s s o r e I n te l a 2 G H z , c o n s is te m a o p e r a t iv o L in u x e Java
6. S u u n a ltro c o m p u t e r i n u m e r i e f fe t t i v i p o t r e b b e r o e s s e r e d iv e r s i, m a la r e la z i o n e fra
lo r o r e s te r e b b e la stessa .
La F ig u r a 1 m o s tr a a n c h e u n g r a f ic o d e l le r ile v a z io n i: c o m e p o t e t e v e d e r e , r a d d o p
p i a n d o la d i m e n s i o n e d e l l ’i n s i e m e d e i d a ti, il t e m p o c h e o c c o r r e p e r o r d in a r li è c ir c a il
q u a d r u p lo .
Figura 1
n M il l is e c o n d i
Tempo im piegato
dall'ordinam ento 10 0 0 0 786
20
per selezione.
20 000 2148
30 000 4796
15 40 000 9192
O 50 00 0 13 321
-TD
C
O 60 000 19 2 9 9
10 -
O
Oh Q
B
5 -
10 20 30 40 50 60
n (migliaia)
Perfarpratica
A q u e s to p u n to si co n sig lia di sv o lg e re gli esercizi E 1 3 .3 e E 1 3 .9 , al te r m in e d el ca p ito lo .
p e rc h é
n{n + 1 )
—;r + —» - 3
2 2
-■ 2 0 0 0 ^
2
= 4
1 2
-•1 0 0 0 ^
2
1 2 5
—rr + —« - 3
2 2
nella c o r r is p o n d e n te n o ta z io n e O - g r a n d e basta s e m p lic e m e n te in d iv id u a re il te rm in e c h e
a u m e n ta p iù ra p id a m e n te ,« -,e ig n o rare il suo c o e ffic ie n te co stan te, 1 / 2 , in d ip e n d e n te m e n te
dal fa tto c h e sia g ra n d e o p ic c o lo .
A b b ia m o o sse rv a to p r im a c h e il n u m e r o e ffe ttiv o d e lle is tru z io n i e s e g u ite dal
p ro c e sso re e l’effettiv a q u a n tità di te m p o c h e il c o m p u te r d e d ic a a esse è all’in c irca
p ro p o rz io n a le al n u m e ro d elle visite d eg li e le m e n ti d e ll’array. F o rse p e r ciasc u n a visita
s e rv o n o u n a d e c in a di is tru z io n i m a c c h in a (in c re m e n ti, c o n fro n ti, le ttu re e s c rittu re nella
m e m o ria ): il n u m e ro d elle is tru z io n i m a c c h in a è q u in d i a p p ro ssim a tiv a m e n te 1 0 * (1 /2 )- m-.
A n c o ra u n a v o lta il c o e ffic ie n te n o n ci in teressa, p e r c u i p o ssia m o d ire c h e il n u m e ro
d elle is tru z io n i m a c c h in a , e q u in d i il te m p o n e c e ssa rio p e r l’o r d in a m e n to , è d e ll’o rd in e
di n^, o v v e ro 0{n^).
R im a n e il triste fatto c h e il ra d d o p p io delle d im e n sio n i d e ll’array q u a d ru p lic a il te m p o
I L'ordinamento per selezione
n e c e ssa rio p e r o rd in a rlo u sa n d o l’o r d in a m e n to p e r se le z io n e . Q u a n d o la d im e n s io n e
I è un algoritmo 0{n^): il raddoppio
|ÌÉ b dimensione delUnsieme di dati
d e ll’a rray a u m e n ta di u n fa tto re 100, il te m p o di o r d in a m e n to a u m e n ta di u n fa tto re
, quadruplica il tempo di elaborazione. 1 0 0 0 0 . P er o rd in a re u n array c o n u n m ilio n e di v o c i (p e r g e n e ra re , ad ese m p io , u n e le n c o
te le fo n ic o ), si im p ie g a u n te m p o 1 0 0 0 0 v o lte p iù lu n g o di q u e llo c h e o c c o rre re b b e p e r
o rd in a re 1 0 0 0 0 v o ci. Se 1 0 0 0 0 v o ci si p o sso n o o rd in a re in circa tre q u a rti di s e c o n d o
(c o m e n el n o stro e se m p io ), allo ra l’o r d in a m e n to di u n m ilio n e di v o ci ric h ie d e b e n p iù
di d u e o re. Q u e s to è u n p ro b le m a : v e d re m o n el p ro ssim o p a ra g ra fo c o m e si p o ssa n o
m ig lio ra re in m o d o sp e tta c o la re le p re sta z io n i d el p ro ce sso di o rd in a m e n to sc e g lie n d o
u n a lg o ritm o p iù sofisticato.
O r d in a m e n t o e r ic e r c a 689
Auto-valutazìone
7 . S e a u m e n ta te d i d ie c i v o lt e la d im e n s io n e d e ll’i n s ie m e d i d a ti, c o m e a u m e n ta il t e m p o
r ic h ie s t o p e r o r d in a r lo c o n l ’a lg o r it m o di o r d in a m e n t o p e r s e le z io n e ?
8 . Q u a n t o d e v e v a ler e ri p e r c h é ( l / 2 ) « 2 sia m a g g io r e d i { 5 / 2 ) n — 3?
9 . N e l P aragrafo 7 .3 .6 s o n o stati p r e se n ta ti d u e a lg o r itm i p e r !’e li m in a z io n e d i u n e le m e n t o
da u n array di lu n g h e z z a n . Q u a n te v is ite a s in g o li e le m e n t i d e ll’array s o n o r ic h ie s te , in
m e d ia , da c ia s c u n a lg o r itm o ?
1 0 . U s a t e la n o t a z i o n e O - g r a n d e p e r d e s c r iv e r e il n u m e r o d i v is it e n e i p r o b le m i d e s c r itti
n e lla d o m a n d a p r e c e d e n te .
11 . U s a n d o la n o t a z i o n e O - g r a n d e , q u a l è il t e m p o r ic h ie s t o p e r v e r ific a r e se u n array è
o r d in a to ?
12 . C o n s i d e r a t e q u e s t o a l g o r i t m o d i o r d i n a m e n t o d i u n a rra y d i l u n g h e z z a ^ .T r o v a t e
l ’e le m e n t o m a s s im o tra i p r im i k e le m e n t i e r im u o v e t e lo d a ll’array u s a n d o il s e c o n d o
a l g o r i t m o p r e s e n t a t o n e l P a ra g r a fo 7 . 3 . 6 , q u i n d i d i m i n u i t e k d i u n ’u n ità e s c r iv e t e
l ’e le m e n t o r im o s so nella p o s iz io n e di in d ic e k d e ll’array. R ip e t e t e la p ro ced u ra , te r m in a n d o
l ’a lg o r itm o q u a n d o k v a le 1 . U s a n d o la n o t a z io n e O - g r a n d e , q u a l è il t e m p o d ’e s e c u z io n e
d e l l ’a lg o r itm o ?
Perfarpratica
A q u e s t o p u n t o si c o n s i g l ia d i s v o lg e r e g li e s e r c iz i R 1 3 . 4 , R 1 3 . 6 e R 1 3 . 8 , al t e r m i n e
d e l c a p i t o lo .
T(M ) = 0 ( / ( m))
S e T { n ) è u n p o l i n o m i o d i g r a d o k in //,a llo r a si p u ò d im o s t r a r e c h e T { n ) = 0 { n ^ ) . N e l
s e g u i t o d e l c a p i t o lo v e d r e m o f u n z i o n i c h e s o n o 0 ( l o g ( / / ) ) o 0 ( / / l o g ( // ) ) , m e n t r e a lc u n i
a l g o r i t m i r i c h i e d o n o u n t e m p o d ’e s e c u z i o n e m o l t o m a g g i o r e . A d e s e m p i o , u n m o d o
p e r o r d in a r e u n a s e q u e n z a c o n s i s t e n e l c a lc o la r e t u t t e le p e r m u t a z i o n i d e i s u o i e l e m e n t i ,
fin q u a n d o n o n se n e tr o v a u n a o r d in a ta : ta le a l g o r i t m o r i c h i e d e u n t e m p o 0 { n \ ) , n o n
p r o p r i a m e n t e e s a lta n te .
L a T a b e lla 1 m o s tr a a lc u n e e s p r e s s io n i O - g r a n d e u s a te d i f r e q u e n t e , in o r d in e c r e s c e n t e .
690 C a p it o l o 13
Tabella 1
Espressione O-grande N om e
A lc u n e es p re s s io n i
0 -g r a n d e d i u tiliz z o 0 (1 ) C o sta n te
fr e q u e n te 0 ( lo g ( « ) ) L ogaritm ica
0 (n ) L ineare
0 ( « Iog(H)) L o g -lin ea re
O(H^) Q uad ratica
O ( h’) C u b ica
0 (2 " ) E sp o n en zia le
0 ( h !) F attoriale
T(«) = Q (/(« ))
T(«) = © (/(«))
a [0 ] a [ l ] . . . a[k]
A d ese m p io , s u p p o n ia m o di in iz ia re c o n q u e s to array:
M ± 16
s fg [n 16
I n fin e ,l’e le m e n to a [4], d i v alo re 7, v ie n e in s e rito nella p o siz io n e c o rre tta e l’o rd in a m e n to
è c o m p le to .
La classe se g u e n te realizza l’a lg o ritm o di o r d in a m e n to p e r in s e rim e n to .
n o n è stata tr o v a ta la p o s i z i o n e in c u i in s e r ir e il n u o v o e l e m e n t o , d o p o d i c h é d o b b i a m o
sp o sta r e v e r s o p o s iz i o n i d i in d ic e m a g g io r e i r im a n e n t i e le m e n t i d e lla p a r te g ià o r d in a ta . D i
c o n s e g u e n z a , v e n g o n o v is ita ti ^ + 1 e l e m e n t i d e l l ’array, p e r c u i il n u m e r o t o t a le d i v is it e è:
-I . ^ . . « • ( « + !) ,
2 + 3 H------- 1- = ------------------- 1
P o s s ia m o , q u i n d i , c o n c l u d e r e c h e l ’o r d i n a m e n t o p e r i n s e r i m e n t o è u n a l g o r i t m o 0 { n ^ ) ,
L'ordinamento per inserimento
c o n u n ’e f f i c ie n z a d e l l o s te s s o o r d i n e d i q u e lla d e l l ’o r d i n a m e n t o p e r s e l e z i o n e .
è un algoritmo 0 ( n ^ .
L’o r d in a m e n t o p e r in s e r im e n t o h a , p e r ò , u n ’in te r e s s a n te c a ra tte ristica : le s u e p r e s ta z io n i
s o n o 0 { n ) se !’array è g ià o r d in a t o (si v e d a l ’E s e r c i z i o R 1 3 . 1 9 ) . T a le p r o p r ie t à è u t ile in
m o l t i ca si p r a tic i, p e r c h é c a p ita s p e s s o d i d o v e r o r d in a r e i n s i e m i d i d a ti g ià p a r z ia lm e n t e
o r d in a t i.
HZ 10 12 17 11 20 32
5 9 10 12 17 8 11 20 32 EE
9 10 12 17 8 11 20 32 tlls l
r 9 10 12 17 11 20 32
10 12 17 11 20 32 r A |s Ì 8 |9 j
Z T I2I17 11 20 32
12 17 20 32
17 r r r 20 32 ilsIslglioriiTia
20 32 8 9 1 0 [ l l | l 2 l 17
32 a F 8 9 10 11 12 17 20
H 1 5 8 9 |l 0 11 1 2 |l7 |2 0 |3 2 |
In e f f e t t i, è p r o b a b ile c h e a b b ia te e s e g u i t o p r o p r io q u e s t o t i p o d i f u s i o n e q u a n d o v i s ie t e
tr o v a ti c o n u n a m i c o a d o v e r o r d in a r e u n a p ila d i f o g li. A v e t e s p a r tito la p ila in d u e m u c c h i ,
c ia s c u n o d i v o i h a o r d in a t o la p r o p r ia m e t à e , p o i , a v e te f u s o i n s i e m e i v o s tr i r is u lta ti.
O r d in a m e n t o e r ic e r c a 693
sort (first);
sort(second);
merge(first, second, a);
}
File MergeSorter.java
File MergeSortDemo.java
import java.util.Arrays;
*f
public class MergeSortDemo
{
public static void main(String[] args)
{
int[] a = ArrayUtil.randomIntArray(20, 100);
System.out.println(Arrays.toString(a));
MergeSorter.sort(a);
System.out.println(Arrays.toString(a));
}
}
Auto-valutazione
13. P e rc h é s o lta n to u n o d e i d u e c ic li while p r e s e n ti al t e r m in e d el m e to d o merge fa
q u alco sa?
14. E se g u ite m a n u a lm e n te !’a lg o ritm o di o rd in a m e n to p e r fu sio n e su ll’a rray 8 7 6 5 4 3
2 1.
15. L’a lg o ritm o di o rd in a m e n to p e r fusione elabora u n array analizzando rico rsiv am en te le
sue d u e m età. D esc riv ete u n a lg o ritm o rico rsiv o analogo c h e calcoli la so m m a di tu tti
gli elem e n ti presenti in u n array.
Perfarpratica
A q u e s to p u n to si c o n sig lia di sv o lg ere gli esercizi R l 3 .1 3 , E l 3 .4 e E l 3 .1 4 , al te rm in e
del ca p ito lo .
n (migliaia)
C i a s c u n p a s s o d e l p r o c e s s o d i f u s i o n e a g g i u n g e a ll’a r ra y a u n e l e m e n t o , c h e p u ò v e n i r e
d a first O d a second: n e lla m a g g i o r p a r t e d e i c a si b i s o g n a c o n f r o n t a r e g li e l e m e n t i in i z i a l i
d e l le d u e m e t à p e r s t a b ilir e q u a le p r e n d e r e . C o n t e g g i a m o q u e s t a o p e r a z i o n e c o m e 3
v i s i t e p e r o g n i e l e m e n t o (u n a p e r a e u n a c ia s c u n a p e r first e second), o v v e r o 3n v isite
in t o t a l e , e s s e n d o n la l u n g h e z z a d e l f a r r a y a. I n o lt r e , a ll’i n i z i o d o b b i a m o c o p i a r e t u t t i
g li e l e m e n t i d a l l ’a r ra y a n e g l i a r ra y first e second, r e n d e n d o n e c e s s a r i e a ltr e 2 n v i s i t e ,
p e r u n to ta le d i 5 « .
S e c h i a m ia m o T{n) il n u m e r o d i v i s i t e n e c e s s a r ie p e r o r d in a r e u n array d i n e le m e n ti
m e d i a n t e il p r o c e s s o d i o r d i n a m e n t o p e r f u s io n e , o t t e n i a m o :
p e r c h é l ’o r d i n a m e n t o d i c ia s c u n a m e t à r i c h i e d e T { n / 2 ) v is it e . In r ea ltà , se n n o n è p a r i
a b b ia m o u n s o tto -a r r a y d i d i m e n s i o n e { ti - l ) / 2 e u n a ltro d i d i m e n s i o n e { n + 1 ) / 2 : s e b b e n e
q u e s t o d e t t a g l io si d im o s t r e r à ir r ile v a n t e ai f in i d e l r is u lt a t o d e l c a lc o l o , s u p p o r r e m o p e r
o r a c h e n sia u n a p o t e n z a d i 2 , d i c i a m o n = 2^”. In q u e s t o m o d o tu t t i i s o t t o - a r r a y si
p o s s o n o d i v id e r e in d u e p a r ti u g u a li.
S f o r t u n a t a m e n t e , la f o r m u la
n o n c i f o r n i s c e c o n c h ia r e z z a la r e l a z i o n e e s is t e n t e fra n e T{n). P e r in d iv id u a r e la n a tu r a
d i ta le r e la z io n e , v a lu t ia m o T{n/2) u s a n d o la stessa f o r m u la , o t t e n e n d o :
Q u in d i
O r d in a m e n t o e r ic e r c a 697
F a c c ia m o lo dì n u o v o :
T (n /4 ) = 2 T ( m/ 8 ) -f S n / 4
q u in d i
T (« ) = 2 X 2 X 2 T (« /8 ) + 5« + 5« + 5 m
T (n) = 2 ^ T (n /2 ^ ) + S n k
R ic o r d ia m o c h e a b b ia m o a ssu n to n —2 ^ ; di c o n s e g u e n z a , p e r k = m,
= n S n l o g 2«
lo g jW = lo g ,„ W / lo g ,„(2) = 3 .3 2 1 9 3 x lo g „,(x )
D i c o n s e g u e n z a , p o s s ia m o d ire c h e l ’o r d in a m e n to p e r fu s io n e è u n a lg o r i tm o 0 (n
lo g (n )).
L’a lg o ritm o d i o r d in a m e n to p e r fu sio n e , c o n p re sta z io n i 0 { n \ o g { n ) ) , è m ig lio re
lo rd in a m e n to p e rfu s io n e
d e ll’a lg o r itm o d i o r d in a m e n to p e r s e le z io n e , a v e n te p r e s ta z io n i 0 ( / i “)? C i p o te te
è un a lg o ritm o 0 (n lo g (n )).
iifu n z io n e nIog(Ai) cresce m o lto più s c o m m e tte re . R ic o r d a te c h e , c o n l’a lg o ritm o 0 ( « “), l’o r d in a m e n to di u n m ilio n e di
le n ta m e n te d i /Al
v a lo ri r ic h ie d e 100^ = 1 0 0 0 0 v o lte il te m p o c h e è n e c e ssa rio p e r o rd in a re 1 0 0 0 0 v alo ri.
C o n l’a lg o ritm o 0{n log(w)), il r a p p o r to è:
1 0 0 0 0 0 0 lo g 100 0 0 0 0
= IOOl - = 150
10 0 0 0 lo g 10 0 0 0 4y
In q u e s t o c a p i t o l o a b b ia m o a p p e n a c o m i n c i a t o a s c a lfir e la s u p e r f i c ie d i q u e s t o
in t e r e s s a n t e a r g o m e n t o . E s i s t o n o m o l t i a lg o r i t m i d i o r d i n a m e n t o , a lc u n i d e i q u a li h a n n o
p r e s t a z io n i p e r s i n o m i g l i o r i d i q u e l l e d e H ’o r d i n a m e n t o p e r f u s io n e , e la c u i a n a lisi p u ò
e s s e r e u n a b e lla sfid a . S e sta te s e g u e n d o u n c o r s o d i s tu d i in in f o r m a t ic a , r iv e d r e t e q u e s t i
im p o r t a n t i a r g o m e n t i in u n c o r s o s u c c e s s iv o .
Mo-valutazione
1 6 . S u lla b a se d e i d a ti t e m p o r a li p r e s e n ta ti n e lla ta b e lla a ll’i n i z i o d i q u e s t o p a r a g r a fo p e r
l’a lg o r it m o di o r d in a m e n t o p e r fu s io n e , q u a n to t e m p o o c c o r r e p e r o rd in a re u n array di
1 ()()()()() valo ri?
1 7 . S e r a d d o p p ia te la d im e n s io n e di u n array, c o m e a u m e n ta il t e m p o r ic h ie s t o p e r o rd in a re
il n u o v o array u s a n d o l ’a lg o r it m o di o r d in a m e n t o p e r fu s io n e ?
Perfarpratica
A q u e s t o p u n t o si c o n s i g l ia d i s v o lg e r e g li e s e r c iz i R 1 3 .7 , R 1 3 .1 6 e R 1 3 . 1 8 , al t e r m i n e
d e l c a p i t o lo .
5 3 2 6 4 1 3 7
E c c o u n a p o s s i b i l e s u d d iv i s i o n e d e lla p o r z io n e : n o t a t e c h e le d u e p a r ti n o n s o n o a n c o r a
sta te o r d in a te .
3 3 2 1 4
V e d r e t e p iù a v a n ti c o m e o t t e n e r e ta le s u d d iv i s i o n e . N e l p r o s s im o p a s so , o r d in a t e c ia s c u n a
p a r te a p p lic a n d o r ic o r s iv a m e n t e il m e d e s i m o a l g o r i t m o : q u e s ta a z io n e o r d in a l ’in te r a
p o r z i o n e o r ig i n a r i a , p e r c h é il m a s s im o e l e m e n t o p r e s e n t e n e lla p r im a p a r te è al p iù
u g u a le al m i n i m o e l e m e n t o p r e s e n t e n e lla s e c o n d a p a r te .
3 3
O r d in a m e n t o e r ic e r c a 699
In m e d i a , l ’a l g o r i t m o q u ic k s o r t ha p r e s ta z io n i 0 {n l o g ( « ) ) . C ’è u n s o l o a sp etto
s f o r t u n a t o n e l l ’a l g o r i t m o q u i c k s o r t : il s u o c o m p o r t a m e n t o n e l c a s o p e g g i o r e è 0 { n ^ ) .
I n o lt r e , se c o m e p i v o t v i e n e s c e l t o il p r i m o e l e m e n t o d e lla r e g i o n e , il c o m p o r t a m e n t o
d i c a s o p e g g i o r e si h a q u a n d o l ’i n s i e m e è g i à o r d i n a t o ; u n a s i t u a z i o n e , in p r a t ic a ,
p i u t t o s t o f r e q u e n t e . S c e g l i e n d o il p i v o t c o n m a g g i o r a t t e n z i o n e p o s s i a m o r e n d e r e
e s t r e m a m e n t e i m p r o b a b i l e l ’e v e n i e n z a d e l c a s o p e g g i o r e : g li a l g o r i t m i q u i c k s o r t “ m e s s i
a p u n t o ” in ta l m o d o s o n o u s a ti m o l t o f r e q u e n t e m e n t e , p e r c h é l e l o r o p r e s t a z i o n i
s o n o g e n e r a l m e n t e e c c e l l e n t i . A d e s e m p i o , il m e t o d o sort d e lla c la s s e Arrays u s a u n
a lg o r itm o q u ic k s o r t.
U n a ltro m i g l i o r a m e n t o c h e v i e n e s o l i t a m e n t e m e s s o in a tto p r e v e d e d i p assare
a ll’u t i l i z z o d e l l ’o r d i n a m e n t o p e r i n s e r i m e n t o q u a n d o !’array è d i p i c c o l e d i m e n s i o n i ,
p e r c h é il n u m e r o t o t a le d i o p e r a z i o n i r ic h i e s t e d a ll’o r d i n a m e n t o p e r i n s e r i m e n t o è , in
tali c a si, i n f e r io r e . La lib r e r ia Java u sa q u e s t o a c c o r g i m e n t o q u a n d o la lu n g h e z z a d e l l ’array
è i n f e r i o r e a s e tte .
13.6 EffettuarerIcercIie
C a p it a m o l t o f r e q u e n t e m e n t e d i d o v e r c e r c a r e u n e l e m e n t o in u n array e , c o m e v is t o
n e l p r o b le m a d e l l ’o r d i n a m e n t o , la s c e lt a d e l l ’a l g o r i t m o m i g li o r e p u ò fare v e r a m e n t e la
d if f e r e n z a .
Q u a n t o t e m p o r ic h i e d e u n a r ic e r c a lin e a r e ? N e l l ’i p o t e s i c h e l ’e l e m e n t o v sia p r e s e n t e
La ricerca lineare trova un valore
n e l l ’a rray a d i l u n g h e z z a ti, la r ic e r c a r ic h i e d e in m e d i a la v is it a d i n / 2 e le m e n ti. S e
in un array eseguendo un numero
di passi 0{n). l ’e l e m e n t o n o n è p r e s e n t e n e l l ’a rray b i s o g n a v is ita r e t u t t i g li e l e m e n t i p e r v e r if ic a r n e
l ’a ss e n z a . In o g n i c a s o , la r ic e r c a lin e a r e è u n a l g o r i t m o 0 { n ) .
E c c o u n a c la s se c h e e s e g u e la r ic e r c a lin e a r e in u n array a d i n u m e r i in t e r i: il m e t o d o
search r e s tit u is c e l ’i n d i c e d e lla p r im a c o r r is p o n d e n z a tr o v a ta , o p p u r e - 1 se il v a lo r e n o n
è p r e s e n t e in a.
File LinearSearcher.java
/* *
Una classe che esegue ricerche lineari in un array.
*/
public class LinearSearcher
{
/**
Cerca un valore in un array usando l'algoritmo
di ricerca lineare.
@param a !'array in cui cercare
@param value il valore da cercare
^return l'indice in cui si trova il valore, oppure -1
se il valore non è presente nell'array
*/
public static int search(int[] a, int value)
{
for (int i = 0; i < a.length; i++)
{
if (a[i] == value) { return i; }
}
return -l;
}
}
File LinearSearchDemo.java
import java.util.Arrays;
import java.util.Scanner;
in t n = in .n e x tIn tO ;
i f (n == - i )
{
done = t r u e ;
}
e ls e
{
i n t pos = L in e a r S e a r c h e r .s e a r c h ( a , n ) ;
S y s te m .o u t . p r in t ln ( " F o u n d i n p o s i t io n '' + p o s );
}
[4 6 , 9 9 , 4 5 , 5 7 , 6 4 , 9 5 , 8 1 , 69, 11, 97, 6 , 85, 61, 88, 29, 65, 83, 88, 45, 88]
E n te r num ber t o s e a rc h f o r , - I to q u it: I l
Found in p o s it io n 8
E n te r num ber t o s e a rc h f o r , - I t o q u i t : 12
Found in p o s i t io n - I
E n te r num ber t o s e a rc h f o r , - I to q u it: - I
[0][1][2][3][4][5][6][7][8][9]
1 4 5 8 9 12 17 20 24 32
v o r r e m m o s a p e r e se il v a lo r e 1 5 è p r e s e n t e al s u o i n t e r n o . R e s t r i n g i a m o la n o s tr a r ic e r c a ,
c h i e d e n d o c i se il v a lo r e si tr o v a n e lla p r im a o n e lla s e c o n d a m e t à d e l l ’array. L’u l t i m o
v a lo r e n e lla p r im a m e t à d e l l ’i n s i e m e , a [ 4 ] , è 9: è p iù p i c c o l o d e l v a lo r e c h e s t ia m o
c e r c a n d o , q u i n d i d o v r e m o c e r c a r e n e lla s e c o n d a m e tà d e l l ’i n s i e m e , c i o è n e lla p o r z i o n e
q u i e v id e n z ia t a in g r ig i o :
[0][ i ][2][3][4][5][6][7][8][9]
1 4 5 8 9 12 17 20 24 32
L’e l e m e n t o c e n t r a le d i q u e s ta s e q u e n z a è 2 0 , q u i n d i il v a lo r e c h e c e r c h ia m o , se c ’è , d e v e
tro v a rsi n e lla s o t t o - s e q u e n z a q u i n u o v a m e n t e e v id e n z ia t a in g r ig i o :
[0][ i ] [2 ][3 ][4 ][5 ][6 ][7][8][9]
1 4 5 8 9 20 24 32
L’u l t i m o v a lo r e d e lla p r im a m e t à d i q u e s ta s e q u e n z a m o l t o b r e v e è 1 2 , c h e è m i n o r e d e l
v a lo r e c h e s t ia m o c e r c a n d o , p e r c u i d o b b i a m o c e r c a r e n e lla s e c o n d a m e tà :
O r d in a m e n t o e r ic e r c a 703
[0 ][ i ][2][3][4][5][6][7][8][9]
I 4 5 8 9 12 20 24 32
È b a n a l e c o n s t a t a r e c h e n o n a b b i a m o t r o v a t o il n u m e r o c e r c a t o , p e r c h é 1 5 17.
S e v o l e s s i m o i n s e r ir e 1 5 n e lla s e q u e n z a , a v r e m m o d o v u t o i n s e r i r l o a p p e n a p r i m a
di a [6 ].
Q u e s t o p r o c e s s o d i r ic e r c a si c h i a m a ricerca b in aria {b in a ry search) o r ic e r c a
La ricerca binaria cerca un valore
p e r b i s e z i o n e p e r c h é a o g n i p a s s o d i m e z z i a m o la d i m e n s i o n e d e l la z o n a d a e s p lo r a r e :
in un array ordinato determinando
t a le d i m e z z a m e n t o f u n z i o n a s o l t a n t o p e r c h é s a p p i a m o c h e la s e q u e n z a d e i v a l o r i è
se si può trovare nella prima
O nella seconda metà dell'array, o r d in a ta .
File BinarySearcher.java
/**
Una classe per eseguire ricerche binarie in un array.
*/
public class BinarySearcher
{
/**
Cerca un valore in un array ordinato
utilizzando l'algoritmo di ricerca binaria.
@param a !'array in cui cercare
@param low il primo indice della zona di ricerca
@param high l'ultimo indice della zona di ricerca
@param value il valore da cercare
@return l'indice in cui si trova il valore cercato, oppure -l
se il valore non è presente nell'array
*/
public static int search(int[] a, int low, int high, int value)
{
if (low <= high)
{
int mid = (low + high) / 2;
if (a[mid] == value)
{
return mid;
}
else if (a[mid] < value)
{
return search(a, mid + I, high, value);
}
else
{
return search(a, low, mid - I, value);
}
704 C a p it o l o 13
else
{
return ~l;
}
T(n) = T{n/2) + 1
T(n/2) = T(n/4) + 1
T{n) = T{n/4) + 2
e, g e n e ra liz z a n d o , si o ttie n e :
T{n) = T{n/2^) + k
T{n) = 1 + log2(«)
Auto-valutazione
1 8 . I m m a g in a te d i d o v e r c erca re u n n u m e r o t e le f o n i c o in u n in s ie m e di u n m i li o n e di d ati.
Q u a n ti p e n s a te d i d o v e r n e e sa m in a r e, m e d ia m e n t e , p e r trovare il n u m e r o ?
1 9 . P e r c h é n e l m e t o d o search n o n si p u ò usare u n c ic l o g e n e r a liz z a to c o m e for (int element
: a)?
2 0 . I m m a g in a te d i d o v e r c erca re u n v a lo re in u n array o r d in a to a v e n te u n m i li o n e e le m e n t i.
U s a n d o !’a l g o r i t m o d i r ic e r c a b in a r ia , q u a n t i e l e m e n t i p e n s a t e d i d o v e r e s a m in a r e ,
m e d ia m e n t e , p e r trovare il v a lo re c h e cerca te?
Perfarpratica
A q u e s t o p u n t o si c o n s i g l ia d i s v o lg e r e g li e s e r c iz i R l 3 . 1 4 , E 1 3 .1 3 e E l 3 . 1 5 , al t e r m i n e
d e l c a p it o lo .
int count = 0;
for (int i = 0; i < a.length; i-r-h)
{
if (a[i] == value) { count-!--!-; }
}
Q u a l è il t e m p o d i e s e c u z i o n e in f u n z i o n e d i n, la l u n g h e z z a d e l l ’a rray a?
P a r t ia m o d a u n ’a n a lisi d e l l o s c h e m a d i v isita a g li e l e m e n t i d e l l ’array. O g n i e l e m e n t o
v i e n e v is it a t o e s a t t a m e n t e u n a v o lt a e , c o m e a u s ilio alla c o m p r e n s i o n e d i q u e s t o s c h e m a ,
i m m a g in a t e !’a rray c o m e se fo s s e u n a s e r ie d i l a m p a d in e , d is p o s t e in fila (si v e d a p a g in a
s e g u e n t e ) . N e l m o m e n t o in c u i l ’e l e m e n t o / - e s i m o v i e n e v is it a t o , a c c e n d i a m o la /- e s im a
la m p a d in a .
O r a p e n s ia m o a c i ò c h e a v v i e n e d u r a n te c ia s c u n a v is ita e c i c h i e d ia m o : p e r u n a v is ita ,
è r ic h i e s t o u n n u m e r o fisso d i a z io n i, i n d i p e n d e n t e da m? In q u e s t o c a s o s p e c i f ic o , è c o s ì .
S e r v o n o p o c h e a z io n i, p e r c h é b is o g n a : l e g g e r e l ’e l e m e n t o , c o n f r o n t a r l o c o n il v a lo r e
c e r c a t o e , q u a n d o n e c e s s a r io , i n c r e m e n t a r e u n c o n t a t o r e .
Q u i n d i , il t e m p o d i e s e c u z i o n e è p a r i a tì v o l t e u n t e m p o c o s t a n t e , p e r c u i è 0 { n ) .
Un ciclo che esegue n iterazioni,
C o s a s u c c e d e se l ’a l g o r i t m o n o n a r riv a s e m p r e alla f in e d e l l ’array? S u p p o n i a m o , ad
ciascuna delle quali è costituita
e s e m p i o , d i v o l e r v e r if ic a r e s e u n d e t e r m i n a t o v a lo r e è p r e s e n t e n e l l ’array, s e n z a c o n t a r e
da un numero fisso di azioni,
richiede un tempo 0(/7). q u a n t e v o l t e v i r ic o r r e :
O r d in a m e n t o e r ic e r c a 707
‘ '1 ^ '
®
\J' - '1 '
®
\l^ ' I ' ^ m
®
' I ^
® - X ^ -X
W '.|/
''¢ /'
©
708 C a p it o l o 13
13.7.2 Algoritmiquadratici
P assiam o o ra a u n caso p iù in te re ssa n te . C o s a su c c e d e se, in c o rris p o n d e n z a di o g n i
visita, le a z io n i sv o lte s o n o “ m o lte ” ? E c c o u n e se m p io : v o g lia m o c e rc a re l’e le m e n to p iù
fre q u e n te in u n array.
S u p p o n ia m o c h e !’array sia q u e sto :
8 7 5 7 7 5 4
8 7 5 7 7 5 4 1 3 2 4 9 12 3 2 5 11 9 2 3 7 8
8 7 5 7 7 5 4
counts: 2 2
1 3 3 3 1
1. C a lc o la tu tti i c o n te g g i.
2. T rova il v alo re m a ssim o tra i c o n te g g i.
3. T rova la p o s iz io n e d el v alo re m a ssim o tra i c o n te g g i.
O r d in a m e n t o e r ic e r c a 709
T j( h) = a t r + b n C
T ^{ n ) = (in e
T ^{ n ) = f a + g
La lo r o s o m m a , c h e r a p p r e s e n ta il t e m p o t o t a le , è:
T {n) = T j(« ) + T ^ ( n ) + T ^ ( n ) = an ~ + + + e + ^
M a è i m p o r t a n t e s o l t a n t o il t e r m i n e d i g r a d o m a s s im o , p e r c u i T { n ) è 0 ( n ~ ) .
A b b i a m o , q u i n d i , s c o p e r t o c h e l ’a l g o r i t m o c h e tr o v a l ’e l e m e n t o p iù f r e q u e n t e in u n
array è 0 ( n ~ ) .
©
y
©
V I
©
'I ''
©
710 C a p it o l o 13
R i u s c i a m o a r is p a r m ia r e t e m p o e v it a n d o d i c o n t a r e p iù v o l t e u n o s te s s o e l e m e n t o ?
C i o è , p r im a d i in iz ia r e a c o n t a r e q u a n t e v o l t e r ic o r r e n e l l ’array il v a lo r e a [ i ] , n o n
d o v r e m m o v e r if ic a r e c h e ta le v a lo r e n o n sia g ià p r e s e n t e n e lla r e g i o n e a [o ] . . . a [ i - i ] ?
F a c c ia m o u n a s tim a d e l t e m p o n e c e s s a r i o a fare q u e s t e v e r i f i c h e a g g i u n t i v e . A l p a s so
/ - e s i m o la q u a n t it à d i la v o r o c h e s e r v e è p r o p o r z io n a le a /. N o n è p r o p r io c o m e n e l
p a r a g r a fo p r e c e d e n t e , d o v e a v e t e v i s t o c h e u n c i c l o a v e n t e ri i t e r a z i o n i , c ia s c u n a d e l le
q u a li r ic h i e d e u n t e m p o 0 ( h ), è 0 { n ^ ) . O r a c ia s c u n a i t e r a z i o n e r i c h i e d e ( s o lt a n t o ) u n
tem p o 0 { i ) .
P e r in t u ir e c o s a s i g n i f i c h i t u t t o c iò , g u a r d ia m o d i n u o v o a lle la m p a d in e . N e l l a s e c o n d a
it e r a z io n e d o b b i a m o is p e z io n a r e d i n u o v o a [o ], n e lla te rz a it e r a z io n e d o b b i a m o is p e z io n a r e
d i n u o v o a [o ] e a [ i ] , e c o s ì v ia . L o s c h e m a è r ip o r t a t o n e lla p a g in a p r e c e d e n t e .
S e o g n i r ig a h a ri l a m p a d i n e , q u e l l e a c c e s e r i e m p i o n o c ir c a m e t à d e l l o s c h e m a
Un ciclo che esegue n iterazioni,
q u a d r a t o , c i o è c i s o n o c ir c a n ^ / 2 l a m p a d i n e a c c e s e : s f o r t u n a t a m e n t e , q u e s t a f u n z i o n e è
la /-esima delle quali richiede
ancora O (n ^ ).
un tempo 0{i), è 0{n^).
M a a b b ia m o u n ’altra id e a c h e fo r s e c i farà r is p a r m ia r e t e m p o . Q u a n d o c o n t i a m o le
r ic o r r e n z e d i a [ i ] n o n c ’è a lc u n b i s o g n o d i i s p e z io n a r e la r e g i o n e a [o ] . . . a [i - i].
I n fa tti, se il v a lo r e a [ i ] n o n è m a i c o m p a r s o n e l l ’array d u r a n t e le i t e r a z i o n i p r e c e d e n t i ,
o t t e n i a m o il c o n t e g g i o c o r r e t t o i s p e z i o n a n d o s o l t a n t o a [ i ] . . . a[n - l ] ; se , i n v e c e , è
g ià c o m p a r s o , a llo r a il c o n t e g g i o !’a b b ia m o g ià c a lc o la t o . C i è d ’a i u t o t u t t o q u e s t o ? In
rea ltà n o , si tratta d i n u o v o d i u n o s c h e m a t r ia n g o la r e , a n c h e se n e l l ’altra d i r e z io n e :
©
\ | / '
T u t t o q u e s t o , p e r ò , n o n s ig n if ic a c h e !’i m p l e m e n t a z i o n e d i q u e s t i m i g li o r a m e n t i n o n sia
in te r e s s a n te . S e la s o l u z i o n e m i g li o r e c h e si r ie s c e a o t t e n e r e p e r u n d e t e r m i n a t o p r o b le m a
O r d in a m e n t o e r ic e r c a 711
è u n a l g o r i t m o 0 ( « “), è c o m u n q u e u t i l e c e r c a r e d i r e n d e r e p iù v e l o c e la su a e s e c u z i o n e .
N o n s e g u i r e m o , p e r ò , q u e s t a stra d a , p e r c h é , in efifetti, n e l p r o s s im o p a r a g r a fo v e d r e m o
c h e p o s s i a m o fa re m o l t o m e g li o .
8 7 5 7 7 5 4 4 5 5 7 7 7 8
Q u e s t a fa se d i o r d i n a m e n t o r i c h i e d e u n t e m p o 0 { n l o g ( « ) ) . S e r iu s c ia m o a c o m p l e t a r e
l ’a l g o r i t m o in u n t e m p o 0 ( « ) , a llo r a a v r e m o tr o v a to u n a s o l u z i o n e m i g li o r e d i q u e l l e
p r e c e d e n ti, c h e era n o 0 { n ^ ) .
P er c a p ir e c o m e q u e s t o sia p o s s ib ile , i m m a g in a t e d i s c a n d ir e !’a rray o r d in a t o . O g n i
v o lt a c h e tr o v a te u n v a lo r e c h e è u g u a le a q u e l l o p r e c e d e n t e , i n c r e m e n t a t e u n c o n t a t o r e .
Q u a n d o , i n v e c e , tr o v a te u n v a lo r e d iv e r s o , m e m o r i z z a t e il v a lo r e d e l c o n t a t o r e e f a t e lo
r ip a r tir e d a z e r o :
values: 4 5 5 7 7 7 8
counts:
1 1 2 1 2 3 1
V e d i a m o il codice:
int count = 0;
for (int i = 0; i < a.length; i++)
{
count++;
if (i == a.length - i || a[i] != a[i + l])
{
counts[i] = count;
count = 0;
}
}
In q u e s t o a l g o r i t m o l ’e s e c u z i o n e d i o g n i i t e r a z i o n e r ic h i e d e u n t e m p o c o s t a n t e , a n c h e
se v is ita d u e e l e m e n t i , c o m e si p u ò v e d e r e n e lla p a g in a s e g u e n t e .
La f u n z i o n e 2 n ( c io è d u e v is it e p e r o g n i i t e r a z io n e ) è 0 { n ) . P o s s ia m o , q u in d i, c a lc o la r e
tu t t i i c o n t e g g i r e la tiv i a u n array o r d in a t o in u n t e m p o 0 { n ) . L’i n t e r o a l g o r i t m o , o r a , è
0 ( n lo g ( n ) ) .
712 C a p it o l o 13
N o t a t e a n c h e c h e , in r ea ltà , n o n c ’è a lc u n b i s o g n o d i m e m o r i z z a r e t u t t i i c o n t e g g i , b a sta
s o l t a n t o t e n e r e tr a c c ia d i q u e l l o m a s s im o v i s t o f i n o a q u e l m o m e n t o ( c o m e v e d r e t e
n e l l ’E s e r c iz io E 1 3 . 1 1 ) .A n c h e q u e s t o è u n m i g li o r a m e n t o c h e v a le la p e n a d i i m p le m e n t a r e ,
m a n o n m o d i f i c a la s tim a O - g r a n d e d e l t e m p o d i e s e c u z i o n e d e l l ’a l g o r i t m o .
26. Se si esegue l’alg o ritm o visto nel Paragrafo 13.7.4 su u n array b id im en sio n ale n x « ,q u a l
è la stim a O -g r a n d e del te m p o rich iesto , in fu n z io n e di «, p e r trovare l’e le m e n to c h e
ric o rre più fre q u en te m e n te?
Perfarpratica
A q u e s to p u n to si c o n sig lia di sv o lg e re gli esercizi R 1 3 .9 , R 1 3 .1 5 , R 1 3 .2 1 e E 1 3 . i l , al
te r m in e d el c a p ito lo .
13.8.1 Ordinamento
La classe Arrays c o n tie n e m e to d i statici, so rt, c h e so n o in g ra d o di o rd in a re array di n u m e ri
Ildasse A rra y s contiene il metodo
in te r i e d i n u m e ri in v irg o la m o b ile . A d e se m p io , p o te te o rd in a re u n array di n u m e ri
di ordinamento che dovrebbe essere
normalmente utilizzato
in te r i sc riv e n d o s e m p lic e m e n te così:
nei programmi Java.
in t[] a = . . .;
A rray s.so rt(a);
ArrayList<String> names = . . .;
C o llec tio n s. sort(nam es);
in t[] a = { 1, 4, 9 };
in t V = 7;
in t pos = Arrays.binarySearch(a, v);
/ / r e s titu is c e -3: v dovrebbe essere in se r ito n ella posizione 2
L’invocazione
a.compareTo(b)
Molte classi della libreria standard di Java (come, ad esempio, la classe String, le classi
involucro per i tipi numerici, le classi che rappresentano una data o il percorso che
identifica un file) implementano !’interfaccia Comparable.
Potete implementare l’interfaccia Comparable anche nelle vostre classi. Ad esempio,
per ordinare una raccolta di nazioni in base alla loro superficie, la classe Country dovrebbe
implementare l’interfaccia Comparable<Country>, dichiarando un metodo compareTo come
questo:
Il metodo compareTo confronta nazioni in base alla superficie. Notate l’uso del metodo
ausiliario Double.compare (visto nella sezione Suggerimenti per la programmazione 10.1),
che restituisce un numero negativo, zero o un numero positivo. Questa soluzione è più
semplice rispetto alla scrittura di una diramazione a tre uscite.
A q u e s t o p u n t o è p o s s i b il e f o r n ir e al m e t o d o A r r a y s .so r t u n array d i n a z io n i:
Country[] countries = new Country[n];
/ / aggiungi nazioni
Quando dovete eseguire ordinamenti o ricerche, usate i metodi delle classi Arrays e
e non altri scritti da voi: gli algoritmi della libreria sono stati ben collaudati
C o llec tio n s
e ottimizzati. L’obiettivo principale di questo capitolo non è stato quello di insegnarvi
a realizzare algoritmi di ordinamento e ricerca. Avete, invece, appreso una cosa più
importante: algoritmi diversi possono avere prestazioni ben diverse, per cui è utile
conoscere meglio la progettazione e l’analisi di algoritmi.
JUitQivalutdzione
27. Perché il metodo Arrays.sort non può ordinare un array di oggetti Rectangle?
28. Cosa bisogna fare per mettere in ordine di saldo crescente un array di oggetti BankAccount?
29. Perché è utile che il metodo A rrays.binarySearch restituisca la posizione in cui inserire
un elemento mancante?
30. Perché il metodo A rra y s.binarySearch restituisce —1 e non —k per segnalare che il
valore cercato non è presente e che, se lo si vuole inserire, va posto nella posizione k?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E 13.12, E 13.16 e E 13.17, al termine
del capitolo.
716 C apitolo 13
. _ ______ _____
Se i caratteri sono diversi, allora il metodo può semplicemente restituire la loro differenza:
Per ordinare un array di nazioni, co u n tries, in base alla superficie, si può ora invocare:
A rrays.sort(coun tries, new CountryComparator());
Per casi come questo esiste anche una comoda abbreviazione. Osservate che il risultato
del confronto dipende da una funzione che mette in corrispondenza ciascuna stringa
con un valore numerico, in questo caso la sua lunghezza. Il metodo statico Comparator,
comparing costruisce proprio un comparatore a partire da un’espressione lambda. Ad
esempio, si può scrivere:
Viene così costruito un comparatore che invoca per entrambi gli oggetti da confrontare
la funzione fornita come parametro, confrontando poi i risultati ottenuti.
718 C apitolo 13
65 46 14 52 38 2 96 39 14 33 13 4 24 99 89 77 73 87 36 81
65 46 14 52
38 2 96 39
14 33 13 4
24 99 89 77
73 87 36 81
14 2 13 5
24 33 14 39
38 46 36 52
65 87 89 77
73 99 96 81
e ricostruiamo !’array intero, leggendo i dati dalla tabella, riga per riga:
14 2 13 5 24 33 14 39 38 46 36 52 65 87 89 77 73 99 96 81
Come si può notare, !’array non è completamente ordinato, ma molti dei numeri di valore
minore si trovano nelle prime posizioni, mentre, al contrario, molti dei numeri di valore
maggiore si trovano nelle ultime posizioni.
O rdinamento e ricerca 719
Ora ripeteremo la procedura finché l’intero array non risulterà ordinato, ogni volta
usando un numero di colonne diverso. Shell usò originariamente colonne il cui numero
era sempre una potenza di due: ad esempio, per ordinare un array di 20 elementi propose
di usare prima 16 colonne, poi 8,4,2 e, infine, una sola colonna. Usando una sola colonna
ci troviamo di fronte a una normale implementazione dell’algoritmo di ordinamento per
inserimento, per cui siamo certi che alla fine !’array risulterà essere ordinato. La cosa in
qualche modo sorprendente è il fatto che le fasi di ordinamento precedenti rendano più
veloce questo ordinamento finale.
In ogni caso, sono state individuate sequenze di numeri di colonne che si comportano
in modo più efficiente, per cui useremo una di queste:
=1
Cl =4
C,= 13
C^ = 40
= 3Ci +1
65 38 14 24 73
46 2 33 99 87
14 96 13 89 36
52 39 4 77 81
Come si può facilmente notare, i numeri appartenenti a una stessa colonna si trovano a
distanza c l’uno dall’altro e la colonna ^-esima è composta dagli elementi a [k ],a [k + c],
a[k + 2 * c], e così via.
Ora modifichiamo l’algoritmo di ordinamento per inserimento in modo che ordini
una di queste colonne. L’algoritmo originario è il seguente:
for (in t i = 1; i < a . length; i++)
{
in t next = a [ i] ;
/ / sposta in avanti t u t t i g l i elem enti maggiori
in t j = i;
while (j > 0 && a [j - i] > next)
{
a [j] = a [j - 1 ];
720 C apitolo 13
}
/ / in s e r is c i l'elem ento
a [j] = next;
}
Il ciclo più esterno ispeziona gli elementi a[i],a[2],e così via.La sequenza corrispondente
alla /e-esima colonna è a[k + c],a[k + 2 * c], ...,quindi il ciclo più esterno diventa:
for (in t i = k + c; i < a. length; i = i + c)
Nel ciclo interno l’algoritmo originario visitava a[j], a[j - i], e così via. Dobbiamo fare
in modo che visiti a[j],a[j - c], ...,quindi diventa:
while (j >= C &&a[j - c] > next)
{
a [j] = a [j - c];
j = j - c;
}
}
O rdinamento e ricerca 721
timer.resetO;
timer.startO;
A rrays.sort(a2);
tim er.stopO ;
i f ( ! A rrays.equals(a, a2))
{
throw new IllegalStateE xception("Incorrect sort resu lt" );
}
tim er .r esetO ;
tim er .sta rtO ;
In sertionS orter. so r t(a 3 );
tim er.stop O ;
C = 3 * C + 1;
con:
C = 2 * c;
Scoprirete che, in questo caso, l’algoritmo è circa tre volte più lento rispetto a quello
che usa la sequenza migliore, ma rimane molto più veloce dell’ordinamento per
inserimento.
Il programma completo si trova nella cartella worked_example_i del Capitolo 13 del
pacchetto dei file scaricabili per questo libro.
e. n + 0.001«'^
f. r? - 1000«^ + 10"
g- n + log (»)
h. + n log (n)
i. 2” +
j. («-' + 2n)/(n" + 0.75)
R13.4. Abbiamo calcolato che il numero effettivo di visite richieste dall’algoritmo di ordinamento
per selezione è
T{n) = (l/2)«2 + (5/2)«- 3
Abbiamo poi stabilito che questo metodo è caratterizzato da una crescita 0{ti-). Calcolate i rapporti
effettivi
T(2000)/T(IOOO)
T(4000)/T(IOOO)
T(IOOOO)/T(IOOO)
* R13.5. Supponiamo che l’algoritmo A impieghi 5 secondi per elaborare un insieme di 1000 dati.
Se l’algoritmo A ha prestazioni 0(«), quanto tempo impiegherà approssimativamente per elaborare
un insieme di 2000 dati? E uno di 10000?
** R13.6. Supponiamo che un algoritmo impieghi 5 secondi per elaborare un insieme di 1000 dati.
Riempite la tabella seguente, che mostra approssimativamente la crescita del tempo di esecuzione
in funzione della complessità dell’algoritmo.
Per esempio, dal momento che 3000^/1000^ = 9, se l’algoritmo fosse 0{ri-), per elaborare un
insieme di 3000 dati impiegherebbe un tempo 9 volte superiore a quello necessario per elaborare
un insieme di 1000 dati, cioè 45 secondi.
R13.7. Ordinate le seguenti espressioni O-grande in ordine crescente.
0(n)
0{n^)
0{rv‘)
0 ( l o g ( h ))
o(,r- log (»))
0 (ii log ( i l ) )
0 ( 2-0
O rdinamento e ricerca 725
0{n\fn)
0(rt*08(”))
R13.8.Qual è Tandamento del tempo di esecuzione dell’algoritmo standard che trova il valore
minimo in un array? E di quello che trova sia il minimo che il massimo?
R13.9. Qual è l’andamento del tempo di esecuzione del seguente metodo, in funzione di la
lunghezza di a? Visualizzate il risultato usando il “ metodo delle lampadine” visto nel Paragrafo 13.7.
public static void swap(int[] a)
{
int i = 0;
int j = a .length - l;
while (i < j)
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
}
}
R 1 3 . 1 0 . Una “ serie” {run) è una sequenza di valori adiacenti ripetuti (si veda l’Esercizio R7.23).
Descrivete un algoritmo 0 { n ) che trovi la lunghezza della serie più lunga in un array.
a. 4, 7, 11, 4, 9, 5, 11, 7, 3, 5
b. - 7 , 6, 8, 7, 5, 9, 0, 11, 10, 5, 8
a. 5, 11, 7, 3, 5, 4, 7, 11, 4, 9
b. 9, 0, 11, 10, 5, 8, -7, 6, 8, 7, 5
c. Ricerca binaria di 8 in - 7 , 1 , 2 , 3 , 5, 7, 1 0 , 13
726 C apitolo 13
R13.15. Il vostro compito consiste nel togliere tutti i duplicati da un array. Per esempio, se !’array
contiene i valori
4 7 11 4 9 5 11 7 3 5
Ecco un semplice algoritmo per risolvere il problema. Esaminate a[i] e contate quante volte ricorre
in a: se il conteggio è maggiore di 1, eliminatelo. Qual è l’andamento del tempo di esecuzione di
questo algoritmo?
R13.16. Modificate l’algoritmo di ordinamento per fusione in modo che elimini gli elementi
duplicati durante la fase di fusione, ottenendo così un algoritmo che elimina gli elementi duplicati
da un array. Si osservi che, alla fine, !’array non ha necessariamente lo stesso ordinamento dell’array
originario. Qual è l’efficienza di questo algoritmo?
R l3.17. Considerate il seguente algoritmo che elimina tutti i duplicati da un array. Ordinate !’array;
poi, esaminando ciascuno dei suoi elementi, per vedere se è presente più di una volta esaminate
l’elemento seguente e, in caso affermativo, eliminatelo. Questo algoritmo è più veloce di quello
dell’Esercizio R 13.15?
R13.18. Mettete a punto un algoritmo 0 (m\og(n)) per eliminare i duplicati da un array nel caso
in cui !’array risultante debba avere lo stesso ordinamento di quello originale. Quando un valore
è presente più volte, devono essere eliminate tutte le sue occorrenze tranne la prima.
R13.19. Perché, quando !’array è già ordinato, l’algoritmo di ordinamento per inserimento è
significativamente più veloce dell’ordinamento per selezione?
R13.20. Considerate la seguente modifica,che migliora le prestazioni dell’algoritmo di ordinamento
per inserimento visto nella sezione Argomenti avanzati 13.2: per ogni elemento dell’array, invocate
Arrays.binarySearch per determinare la posizione in cui va inserito. Questo miglioramento ha un
impatto significativo sull’efficienza dell’algoritmo?
R l3.21. Considerate il seguente algoritmo, noto come ordinamento a bolle {bubble sort):
Finché !'array non è ordinato
Per ogni coppia di elementi adiacenti
Se la coppia non è ordinata
Scambiai suoi elementi.
infatti, una sequenza di messaggi di posta elettronica {e-mait): se prima li ordinate in base alla data
e, poi, li ordinate di nuovo in base al nome del mittente, sarebbe davvero opportuno che la seconda
procedura di ordinamento preservasse l’ordine relativo tra gli elementi indotto dalla prima pro
cedura, in modo che l’utente possa vedere consecutivamente tutti i messaggi che hanno un certo
mittente, ordinati tra loro in base alla data. L’algoritmo di ordinamento per selezione è stabile? E
l’algoritmo di ordinamento per inserimento? Perché?
R13.24. Descrivete un algoritmo che in un tempo 0{n) ordini un array di n byte (cioè di numeri
compresi tra -128 e 127). Suggerimento: usate un array di contatori.
R l3.25. Dopo aver rappresentato le pagine di un libro con una sequenza di array di parole, do
vete costruire il relativo indice analitico (che è un array di parole ordinato), ciascun elemento del
quale è associato a un array ordinato di numeri: le pagine in cui tale parola compare. Descrivete
un algoritmo che costruisca tale indice e fate una stima O-grande del suo tempo di esecuzione
in funzione del numero totale di parole.
R13.26. Dati due array, ciascuno dei quali contiene tt numeri interi, descrivete un algoritmo 0{ti
\og{n)) che determini se hanno (almeno) un elemento in comune.
R13.27. Dato un array contenente n numeri interi e un valore v, descrivete un algoritmo 0{n
\og{n)) che determini se nell’array sono presenti due valori, x e y, la cui somma sia uguale a v.
R13.28. Dati due array, ciascuno dei quali contiene n numeri interi, descrivete un algoritmo 0{n
log(«)) che determini tutti gli elementi che hanno in comune.
R13.29. Immaginate di modificare l’algoritmo quicksort, descritto nella sezione Argomenti avan
zati 13.3, in modo che selezioni come pivot l’elemento centrale dell’array anziché il suo primo
elemento. Quali sono le prestazioni di questa variante dell’algoritmo quando viene eseguito su un
array già ordinato?
R13.30. Immaginate di modificare l’algoritmo quicksort, descritto nella sezione Argomenti avan
zati 13.3, in modo che selezioni come pivot l’elemento centrale dell’array anziché il suo primo
elemento. Individuate una sequenza di valori per il cui ordinamento questo algoritmo richieda
un tempo 0(rr).
E 13.1. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di
numeri interi in ordine decrescente.
E13.2. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di
monete in base al loro valore.
E13.3. Scrivete un programma che generi automaticamente la tabella dei tempi di esecuzione
dell’ordinamento per selezione. Il programma deve chiedere i valori minimo e massimo di n e il
numero di misurazioni da effettuare, per poi attivare tutte le esecuzioni.
E13.4. Modificate l’algoritmo di ordinamento per fusione in modo che ordini un array di stringhe
in ordine lessicografico.
E13.5. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di oggetti
che implementano l’interfaccia Measurable vista nel Capitolo 10.
E13.6. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di oggetti
che implementano l’interfaccia Comparable (nella versione non generica, cioè senza parametro di tipo).
728 C apitolo 13
E13.7. Modificate Talgoritmo di ordinamento per selezione in modo che ordini un array di oggetti,
ricevendo un parametro di tipo Comparator (nella versione non generica,cioè senza parametro di tipo).
E13.8. Scrivete un programma per consultare l’elenco del telefono. Leggete un insieme di dati
contenente 1000 nomi e i relativi numeri di telefono, memorizzati in un file che contiene i dati in
ordine casuale. Gestite la ricerca sia in base al nome sia in base al numero di telefono, utilizzando
una ricerca binaria per entrambe le modalità di consultazione.
E13.9. Scrivete un programma che misuri le prestazioni dell’algoritmo di ordinamento per
inserimento descritto nella sezione Argomenti avanzati 13.2.
E13.10. Implementate !’algoritmo di ordinamento a bolle descritto nell’Esercizio R13.21.
E13.il. Implementate !’algoritmo descritto nel Paragrafo 13.7.4, tenendo però traccia solamente
del valore avente la frequenza più elevata fino a quel momento:
int mostFrequent = 0;
int highestFrequency = -l;
for (int i = 0; i < a .length; i++)
Conta le occorrenze di a[i] ina [i + i] . . . a[n - i]
Se ricorre più di highestFrequency volte
highestFrequency = Quelconteggio
mostFrequent = a [i]
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
14
Java Collections Framework
« in te rfa c e» « in te rfa c e»
C o llec tio n Map
P riority
A rrayL ist Stack LinkedList HashSet TreeSet
Queue
Alla radice della gerarchia si trova l’interfaccia C o llec tio n , che contiene metodi per ag
giungere e rimuovere elementi dalla raccolta, oltre ad altri, come elencato nella Tabella 1.
Dato che tutte le raccolte implementano questa interfaccia, i suoi metodi sono disponibili
in tutte le classi che rappresentano raccolte di vario tipo. Ad esempio, il metodo s iz e
restituisce il numero di elementi presenti in qualsiasi raccolta.
L’interfaccia L ist descrive un’importante categoria di raccolte. In Java, una lista
(list) è una raccolta che ricorda l’ordine relativo tra i propri elementi (come nella Figura
2). La classe A rrayList, ad esempio, implementa l’interfaccia L ist: un oggetto ArrayList
contiene semplicemente un array che si espande quando serve. Se non siete partico
larmente preoccupati dell’efficienza dei vostri programmi, potete usare un ArrayList
ogni volta che vi serve una raccolta di oggetti, ma molte operazioni abbastanza comuni
sono inefficienti quando si usano tali vettori: in particolare, per aggiungere o eliminare
un elemento bisogna spostare tutti gli elementi che si trovano in posizioni di indice
maggiore.
La libreria di Java mette a disposizione un’altra classe, LinkedList, che implementa a
sua volta l’interfaccia L ist. Diversamente da un vettore, una lista concatenata (in inglese,
lifiked list) consente di inserire e rimuovere elementi in posizioni intermedie della lista
in modo efficiente: ne parleremo nel prossimo paragrafo.
B fIiIH ia iifa ' Iao
Si usa una lista quando si vuole preservare l’ordine relativo tra gli elementi. Ad esempio,
Una lista (//st) è una raccolta
i libri posti su uno scaffale della libreria possono essere ordinati per argomento: una
che ricorda l'ordine relativo
tra i propri elementi.
lista è una struttura adeguata per una tale raccolta, perché in c]uesto caso l’ordine tra gli
elementi è importante.
In molte applicazioni, però, non siamo affatto interessati all’ordine tra gli elementi di
una raccolta. Considerate un venditore di libri per corrispondenza, che gestisce soltanto
ordini postali: senza clienti che girano tra gli scaffali, non c’è alcun bisogno di ordinare
i libri per argomento. Una raccolta di elementi privi di un ordinamento intrinseco è un
insiem e (set, Figura 3).
Dal momento che un insieme non tiene traccia dell’ordine tra i propri elementi,
Un insieme (set) è una raccolta
non ordinata di elementi
li può disporre in modo da aumentare l’efficienza di operazioni come la ricerca,
non duplicati. l’inserimento e la rimozione di elementi, e gli informatici teorici hanno ideato
meccanismi adatti a questo. La libreria di Java contiene classi che sono basate su due
di tali strategie: le tabelle hash e gli alberi di ricerca binari. In questo capitolo scoprirete
come scegliere una o l’altra.
Un altro modo per aumentare l’efficienza di una raccolta consiste nel ridurre il
numero di operazioni che può compiere. Una pila (stack) ricorda l’ordine tra i propri
elementi, ma non consente l’inserimento di nuovi elementi in posizioni arbitrarie: si
possono aggiungere elementi soltanto in cima (Figura 4).
In una coda (queue) gli elementi si aggiungono alla fine (cioè “in coda”, tail) e si
eliminano all’inizio (head). Ad esempio, potete gestire una coda di libri, aggiungendo alla
fine della coda i libri che dovete leggere e prendendo il libro che si trova all’inizio della
coda ogni volta che avete tempo di iniziarne uno. Una coda prio ritaria (priority queue)
è una raccolta che non ricorda l’ordine tra i propri elementi (cioè “una raccolta non
ordinata”) ma esegue in modo molto efficiente l’operazione di eliminazione dell’elemento
avente priorità massima. Una coda prioritaria può essere utile per organizzare i vostri
impegni di lettura: ogni volta che avete tempo di leggere un libro, estraete dalla raccolta
quello avente la massima priorità e leggetelo. Nel Paragrafo 14.5 parleremo di pile, code
e code prioritarie.
Infine, una m appa (map) gestisce associazioni tra chiavi (key) e valori (value): ogni
Una mappa (map) gestisce
associazioni tra chiavi e valori.
chiave della mappa è associata a un valore, come nella Figura 5. La mappa memorizza al
proprio interno le chiavi, i valori e le relative associazioni.
732 C apitolo 14
Figura 5
Chiavi
Una mappa che associa a
ogni libro (il valore)
il relativo codice a barre
(la chiave).
Valori
Come esempio consideriamo una biblioteca che assegna a ogni libro un codice a barre. Il
programma utilizzato per tenere traccia dei libri che escono e rientrano nella biblioteca
ha bisogno di cercare il libro associato a un particolare codice a barre e una mappa che
associ i codici a barre ai libri può risolvere questo problema. Parleremo di mappe nel
Paragrafo 14.4.
In questo capitolo useremo la “sintassi a diamante” per costruire esemplari di classi
generiche (si veda la sezione Argomenti avanzati 7.5). Ad esempio, per costruire un vettore
di stringhe scriveremo:
Osservate che dopo new ArrayList, nella parte destra, c’è una coppia di parentesi angolari
priva di contenuto: il compilatore deduce che si voglia costruire un vettore di stringhe
analizzando la parte sinistra deU’enunciato.
Auto-valutazione
1. Un’applicazione che gestisce il registro di classe memorizza una raccolta di valutazioni:
dovrebbe usare una lista o un insieme?
2. Un sistema informativo studentesco memorizza una raccolta di informazioni relative a
ciascuno studente di un ateneo: dovrebbe usare una lista o un insieme?
3. Per quale motivo per organizzare i libri che dovete leggere è meglio usare una coda
piuttosto che una pila?
4. Come potete vedere nella Figura 1, nel Java Collections Framework le mappe non sono
raccolte: per quale motivo?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R l 4.1, R l 4.2 e R l 4.3, al termine
del capitolo.
Una lista concatenata (linked list) o catena è una struttura che memorizza una sequenza
di oggetti e che consente di aggiungere e di rimuovere in modo efficiente elementi in
qualsiasi posizione, anche intermedia, della sequenza. Nei prossimi paragrafi vedrete
come la lista concatenata gestisce i propri elementi e imparerete a usare tale struttura nei
vostri programmi.
Figura 6
Una lista concatenata
734 C apitolo 14
Figura 7
Inserimento di un nodo
in una lista concatenata
Figura 8
Eliminazione di un nodo
da una lista concatenata
Quando viene inserito un nuovo nodo in una lista concatenata, devono essere aggiornati
Aggiungere e rimuovere elementi
in una data posizione di una lista
soltanto i riferimenti nei nodi vicini (si veda la Figura 7). Lo stesso accade quando si
concatenata è un'operazione
elimina un nodo (si veda la Figura 8). Dove sta l’insidia? Le liste concatenate consentono
efficiente. di eseguire in modo efficiente inserimenti e rimozioni, ma l’accesso agli elementi può
essere poco efficiente.
Se, ad esempio, volete localizzare il quinto elemento, dovete necessariamente visitare
i primi quattro: se il vostro algoritmo ha la necessità di accedere agli elementi secondo la
modalità di accesso casuale, questo è un problema. 11 termine “accesso casuale” {random
access) viene usato in informatica per descrivere uno schema di accesso in cui gli elementi
vengono visitati in ordine arbitrario (non necessariamente casuale); al contrario, con
l’accesso sequenziale si visitano gli elementi in sequenza.
Ovviamente, se visitate gli elementi principalmente in successione (per esempio, per
Visitare in sequenza gli elementi
di una lista concatenata è efficiente,
visualizzarli o per stamparli), la scarsa efficienza dell’accesso casuale non è un problema.
ma accedervi secondo la modalità
Ricorrete alle liste concatenate quando siete interessati soprattutto all’efficienza degli
di accesso casuale non lo è. inserimenti e delle eliminazioni e non avete bisogno di accedere agli elementi in modo
casuale.
Figura 9
Una visione astratta Posizione iniziale di L istIte r a to r
di un iteratore operante
su una lista
Dopo !'inserimento di D
Per creare un iteratore che operi su una lista utilizzate il metodo lis t it e r a t o r della classe
LinkedList:
LinkedList<String> employeeNames = . . .;
ListIterator<String> iter a to r = employeeNames.l i s t Ite r a to r ();
Notate che anche la classe iteratore è una classe generica: un oggetto L istIterator< Strin g>
scandisce le posizioni all’interno di una lista concatenata di stringhe, mentre un
ListIterator<Book> visita gli elementi di una LinkedList<Book>.
L’iteratore punta inizialmente alla posizione che precede il primo elemento, poi lo
potete spostare invocando il suo metodo next:
736 C apitolo 14
ite r a to r .n ext();
Il metodo n ext restituisce l’elemento sopra cui transita l’iteratore durante il suo
avanzamento: usando un iteratore di tipo L istIterator< S trin g> , il metodo next restituisce
un riferimento di tipo String e, in generale, il tipo di dato restituito dal metodo next
corrisponde al tipo parametrico usato nella lista (che, a sua volta, ovviamente corrisponde
al tipo di oggetti contenuti nella lista stessa).
Potete visitare tutti gli elementi di una lista concatenata di stringhe usando questo
ciclo:
while (iterator.h asN extO )
{
String name = ite r a to r .n e x t();
Elabora name
}
Se dovete semplicemente visitare tutti gli elementi di una lista concatenata, potete più
sinteticamente usare un ciclo for esteso:
for (String name : employeeNames)
{
Elabora name
}
In questo caso non avete nemmeno bisogno di pensare che esistano gli iteratori: dietro le
quinte, il ciclo for esteso usa un iteratore per visitare tutti gli elementi della lista.
I nodi della classe LinkedList contengono due collegamenti, uno verso l’elemento
successivo e uno verso il precedente: una lista di questo genere è detta lista doppiamente
concatenata {douhly-linked list) e potete anche usare i metodi previous e hasPrevious
dell’interfaccia L istIte r a to r per spostare !’iteratore all’indietro.
II metodo add aggiunge un oggetto subito dopo la posizione attuale dell’iteratore,
quindi sposta la posizione dell’iteratore in modo che si venga a trovare dopo il nuovo
elemento.
ite r a to r . add ( ''Duliet ") ;
Il metodo remove elimina l’oggetto che era stato restituito dalFultima invocazione
di next o previous. Per esempio, questo ciclo elimina tutti gli oggetti che soddisfano una
determinata condizione:
while (it e r a t o r .hasNextO)
{
String name = ite r a to r .n e x t();
i f (name soddisfa la condizione)
ite r a to r . removeO;
}
Quando invocate remove dovete fare molta attenzione, perché può essere invocato una
sola volta dopo aver invocato next o previou s, e non lo si può invocare subito dopo
aver invocato add. Se invocato in modo improprio, il metodo lancia I l l e g a l S t a t e -
Exception.
La Tabella 3 riassume i metodi dell’interfaccia L is tite r a to r , che estende la più
generica interfaccia Ite r a to r , che è adatta alla scansione di qualunque raccolta, non
soltanto una lista. La tabella specifica quali metodi sono disponibili nella sola interfaccia
L is tite r a to r .
Ecco un programma dimostrativo che inserisce stringhe in una lista e, quindi, la percorre
iterativamente, aggiungendo e rimuovendo elementi. Alla fine viene visualizzato il
contenuto dell’intera lista. I commenti segnalano la posizione dell’iteratore.
File ListDemo.java
import j a v a .u t i l.LinkedList;
import j a v a .u t i l.L istIte r a to r ;
*/
public c la ss ListDemo
{
public s ta tic void main(String[] args)
{
LinkedList<String> s ta ff = new LinkedList<>();
S ta ff .addLast( "Diana") ;
S ta ff . addLast( "Harry") ;
S taff.addLast( "Romeo") ;
s t a f f . addLast("Tom");
iterator.removeO; / / DHlN| T
Auto-valuta2iQne
5. L e lis te c o n c a t e n a t e o c c u p a n o p iù s p a z io in m e m o r ia d e g li array c h e c o n t e n g o n o lo
ste sso n u m e r o d i e le m e n ti?
6 . P e r c h é c o n gli array n o n a b b ia m o b is o g n o d i itera to ri?
7 . N e l l ’ip o te s i c h e la lista c o n c a te n a ta l e t t e r s c o n t e n g a in iz ia lm e n t e g li e le m e n t i "A", "B",
"C" e "D", d is e g n a te il c o n t e n u t o d ella lista e la p o s iz io n e d e ll’iter a to re i t e r d o p o c ia scu n a
d e lle s e g u e n t i o p e r a z io n i:
L istIterator< S trin g> i t e r = l e t t e r s . l i s t I t e r a t o r ( ) ;
iter .n ex tO ;
iter .n ex tO ;
iter. r e m o v e O ;
iter .n ex tO ;
Java C ollections F ramework 739
8. Scrivete un ciclo che elimini dalla lista concatenata di stringhe words tutte le stringhe
aventi lunghezza inferiore a quattro.
9. Scrivete un ciclo che visualizzi tutti gli elementi di posizione dispari (contando le posizioni
da zero come gli indici, cioè il secondo elemento, il quarto, ecc.) della lista concatenata
di stringhe words.
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R l 4.5, R l 4.8 e E l4.1, al termine del
capitolo.
14J_ Insiemi
Come detto nel Paragrafo 14.1, un insieme {set) organizza i propri valori in modo
da ottimizzare l’efficienza delle operazioni e questo potrebbe non coincidere affatto
con l’ordine in cui gli elementi vi sono stati inseriti. L’inserimento e la rimozione di
elementi sono operazioni più efficienti in un insieme di quanto non lo siano in una
lista concatenata.
Nei paragrafi che seguono imparerete a scegliere una delle implementazioni di insieme
disponibili e a scrivere codice che usa insiemi.
Standardizzazione tari, come Internet EngineeringTask scrisse un proprio standard che codi
Ogni giorno potete constatare i Force (IETF) e World Wide Web ficava semplicemente ciò che i suoi
vantaggi della standardizzazione. Consortium (W3C). IETF si occupa prodotti facevano in quel momento,
Quando comprate una lampadina, di rendere standard molti protocolli anche se tale formato era diffusamente
potete essere certi che si avvite di Internet, come quello che riguar ritenuto incoerente e troppo com
rà correttamente nel portalampada da lo scambio di messaggi di posta plesso (la sua descrizione occupava
senza averlo misurato a casa e senza elettronica, mentre W3C definisce circa 6000 pagine). Inizialmente l’a
misurare la lampadina nel negozio. Al lo standard per il linguaggio HTML zienda sottopose lo standard a ECMA
contrario, potete aver conosciuto la (Hypertext Markup Language), che (European Computer Manufacturers
scomodità della mancanza di standard descrive il formato delle pagine Web. Association), che lo approvò dopo
se avete comprato una torcia elettrica Questi standard sono stati fondamen una discussione assai contenuta, do
con una lampadina non standard: le tali per la creazione del World Wide podiché ISO lo rese operativo come
lampadine di ricambio per tale torcia Web come una piattaforma aperta, “standard già esistente”, evitando di
sono solitamente difficili da trovare non controllata da nessuna azienda. sottoporlo alla consueta procedura di
e costose. Molti linguaggi di programma revisione tecnica.
1 programmatori hanno la stessa zione, come C++ e Scheme, sono Per motivi analoghi, Sun Mi
voglia di standardizzazione. Conside stati standardizzati da organizzazioni crosystems, dopo aver inventato Java,
riamo fimportante obiettivo dell’in indipendenti, come ANSI (Ameri non acconsentì mai a una sua stan
dipendenza dalla piattaforma per i can National Standards Institute) e dardizzazione da parte di un’orga
programmi Java: dopo aver compilato ISO (International Organization for nizzazione terza, bensì iniziò una
un programma Java, potete eseguire Standardization). ANSI e ISO sono propria procedura, coinvolgendo
il risultato prodotto dalla compi associazioni di professionisti dell’in altre aziende ma rifiutandosi di per
lazione in qualsiasi computer che dustria che sviluppano standard per dere il controllo sullo standard stesso.
disponga di una macchina virtuale qualsiasi cosa, dalle dimensioni e for Ovviamente molte importanti
Java. Perché questo possa accadere, me di pneumatici e carte di credito tecnologie non sono affatto stan
la macchina virtuale Java deve essere ai linguaggi di programmazione. dardizzate. Si consideri il sistema
definita in modo molto preciso: se Molti standard sono stati svilup operativo Windows: sebbene venga
tutte le macchine virtuali non si pati da esperti che lavorano in molte definito come uno standard di fatto
comportassero esattamente allo stes aziende diverse, oltre che da proget (defacto), non è affatto uno standard.
so modo, il motto “scrivi una volta, tisti privati, con l’obiettivo di creare Nessuno ha mai tentato di definire
esegui dappertutto’’ {write once, run un insieme di regole che codifichino formalmente cosa dovrebbe essere
anywhere) si tramuterebbe in “scrivi le strategie migliori, ma a volte gli il sistema operativo Windows, il cui
una volta, collauda dappertutto’’. Per standard sono frutto di lunghi con comportamento cambia secondo
ché diversi gruppi di programmatori tenziosi. Nel 2005 Microsoft iniziò a il volere del suo produttore: ciò è
possano realizzare macchine virtuali perdere contratti governativi perché i perfetto per Microsoft, perché rende
compatibili, la macchina virtuale suoi clienti si preoccupavano del fatto impossibile per altri creare la propria
deve essere standardizzata, cioè c’è che molti dei loro documenti veniva versione di Windows.
bisogno di qualcuno che dia una no archiviati usando formati proprie Nella vostra carriera di infor
definizione della macchina virtuale tari e non descritti pubblicamente. matici ci saranno molte occasioni
e del suo comportamento previsto. Invece di fornire conseguentemente in cui dovrete prendere una deci
Chi crea gli standard? Alcuni supporto a formati standard già esi sione sull’aderenza a un particola
degli standard di maggiore successo stenti O collaborare con un gruppo re standard. Facciamo un semplice
sono stati creati da gruppi di volon di lavoro per migliorarli, Microsoft esempio: in questo capitolo abbiamo
JA V A V -U L L tL IIU N b TKAIVltVVUKK /H I
usato le raccolte di elementi definite usare queste classi nel vostro codice o mentre nel secondo caso altri pro
nella libreria standard di Java, ma a sarebbe meglio realizzare un insieme grammatori potrebbero avere diffi
molti informatici queste classi non di raccolte migliori? Se scegliete la colta nella comprensione del vostro
piacciono, per molti motivi legati a prima opportunità dovete fare i conti codice, perché non hanno familiarità
^ A WA AA ^ AAA V-/ AVA AAA V ATA ■^ ^ ^ ^ AAAACA V -/^ ^ V-/A V VAAAAVVA VAV-/ T V- VV^ AVAA W A V> V-/ AAVA W V - / V - V- y ^ V-I-V-AAV- A l V-/ AA
problemi di progettazione. Dovreste con una realizzazione non ottimale, con le vostre classi.
Per poter usare una tabella hash gli elementi devono disporre di un metodo, che si
chiama hashCode, che calcoli tali numeri interi. Inoltre, gli elementi devono appartenere
a una classe che abbia definito in modo appropriato il metodo equals (si veda il
Paragrafo 9.5.2).
Molte classi della libreria standard, tra cui S trin g, In teger, Double, Point, Rectangle,
Si possono usare insiemi realizzati
Color e tutte le classi di tipo raccolta implementano tali metodi. Di conseguenza,
mediante tabella hash quando
devono, ad esempio, contenere
potete certamente costruire oggetti di tipo HashSet<String>, HashSet<Rectangle> o anche
oggetti di tipo S tr in g , In te g e r , HashSet<HashSet<Integer>>.
D o u b le ,P o in t, R e c ta n g le , Se volete usare un insieme di elementi che siano esemplari di una classe progettata da
0 C o lo r. voi, ad esempio un insieme di libri di tipo HashSet<Book>, dovete definire, nella classe Book, i
metodi hashCode e equals. C ’è, però, un’eccezione a questa regola: se tutti gli elementi sono
distinti (ad esempio, se il programma non userà mai due oggetti Book che rappresentino
libri con lo stesso titolo e lo stesso autore), allora potete semplicemente ereditare i metodi
hashCode e equals della classe Object.
La classe TreeSet usa una strategia differente per disporre gli elementi al proprio
interno: li tiene ordinati. Ad esempio, in un insieme di libri questi potrebbero essere
ordinati in base all’altezza, oppure in ordine alfabetico per autore o titolo. Gli elementi
non vengono memorizzati in un array, perché questo renderebbe troppo inefficiente
l’inserimento e la rimozione di elementi: li memorizza in nodi, come in una lista
concatenata. 1 nodi, però, non vengono disposti in una sequenza lineare, bensì in una
struttura a forma di albero.
Per poter usare un TreeSet , deve essere possibile confrontare gli elementi tra loro,
Si possono usare insiemi realizzati
mediante albero di ricerca binario
per determinare quale sia il più “grande”. Si può, quindi, usare un TreeSet per contenere
con elementi di qualunque classe
oggetti di tipo String o In teger, cioè classi che implementano l’interfaccia Comparable, di
che implementi !Interfaccia cui abbiamo parlato nel paragrafo 10.3 (dove abbiamo anche discusso come si possa
C om parable, come S tr in g realizzare il metodo di confronto anche in classi progettate autonomamente).
0 In te g e r. Come regola di base, usate un insieme di tipo TreeSet quando avete la necessità di
visitare gli elementi dell’insieme in ordine, altrimenti usate un HashSet: se la funzione di
hash è stata progettata bene, l’insieme risulta un po’ più efficiente.
Quando costruite un insieme di tipo HashSet o TreeSet, memorizzatene il riferimento
in una variabile di tipo Set, così
Set<String> names = new HashSet<>();
oppure cosi
Set<String> names = new TreeSet<>();
Dopo aver costruito l’oggetto che funge da raccolta, non importa più sapere quale
realizzazione dell’insieme venga usata, ci serve solo conoscere la sua interfaccia.
742 C apitolo 14
names.add("Romeo");
names. remove("Juliet");
Come in matematica, in Java una raccolta di tipo insieme rifiuta Tinserimento di elementi
Gli insiemi non contengono elementi
duplicati. L'aggiunta alUnsieme
duplicati. L’aggiunta di un elemento non ha alcun effetto se l’elemento è già presente
di un elemento duplicato di un altro
nell’insieme. Analogamente, il tentativo di rimozione di un elemento che non appartiene
elemento già presente nelUnsieme all’insieme viene ignorato.
viene semplicemente ignorata. Il metodo contains verifica se un elemento appartiene all’insieme:
i f (names.contains("D uliet")) . . .
Un iteratore visita gli elementi di un insieme nell’ordine in cui questi sono memorizzati
Un iteratore visita gli elementi
di un insieme nell'ordine in cui questi
alTinterno dell’insieme e questo non coincide necessariamente con l’ordine in cui gli
sono memorizzati airmterno
elementi vi sono stati inseriti. In un insieme realizzato mediante tabella hash gli elementi
dellìnsieme stesso, in relazione vengono visitati in ordine apparentemente casuale, perché il codice di hash li distribuisce
al tipo di implementazione. tra i diversi gruppi in tal modo; visitando, invece, gli elementi di un insieme realizzato
mediante albero di ricerca binario, questi appaiono in ordine, anche se sono stati inseriti
in ordine diverso.
C ’è una differenza importante tra l’oggetto di tipo Ite ra to r che viene fornito da un
Non si può aggiungere un elemento
a un insieme usando una specifica
insieme e quello di tipo L istIte r a to r restituito da una lista. L’oggetto L istIte r a to r ha un
posizione di un iteratore.
metodo add per aggiungere un elemento alla lista nella posizione in cui si trova !’iteratore,
mentre l’interfaccia Ite ra to r non ha tale metodo: non ha senso aggiungere un elemento
in una specifica posizione di un insieme, perché l’insieme può ordinare gli elementi al
proprio interno come preferisce. Quindi, si aggiunge sempre un elemento direttamente
all’insieme, mai a un iteratore delTinsieme.
Java C ollections F ramework 743
}
names . remove( "Romeo") ; Ora names.sizeO vale 1.
names. remove("Duliet") ; Rimuovere un elemento che non è presente non è un errore: l’invocazione del metodo
non ha nessun effetto.
FileSpellCheck.jdva
import j a v a .u t i l.HashSet;
import java.u til.S can n er;
import ja v a .u til.S e t;
import ja v a .io .F ile ;
import j a v a .io .FileNotFoundException;
/**
Verifica quali parole di un file non sono presenti in un dizion ario.
*/
public c la ss Spellcheck
{
744 C apitolo 14
Auto-valutazione
10. Gli array e Ie liste memorizzano l’ordine in cui vengono aggiunti gli elementi, mentre gli
insiemi non lo fanno. Per quale motivo in alcune situazioni si preferisce utilizzare insiemi
invece di array o liste?
11. Perché gli iteratori per insiemi sono diversi dagli iteratori per liste?
12. Cosa c’è di sbagliato nel codice seguente che vorrebbe verificare se l’insieme Set<String>
S contiene gli elementi "Tom", "Diana" e "Harry"?
i f (s.to S tr in g ().e q u a ls(" [T o m , Diana, Harry]")) . . .
13. Come realizzereste correttamente la verifica descritta nella domanda precedente?
14. Scrivete un ciclo che visualizzi tutti gli elementi che si trovano sia in Set<String> s sia in
Set<String> t.
15. Modificando la riga 40 del programma SpellCheck in modo che usi un TreeSet invece di
un HashSet, come cambiano le informazioni che vengono visualizzate?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E 14.3, E 14.12 e E14.13, al termine
del capitolo.
HashSet o
è considerato un buono stile di programmazione.
TreeSet
14.4 Mappe
Una m appa (map) memorizza associazioni tra un insieme di chiavi (key) e una raccolta
Le classi HashMap e TreeMap
implementano l'Interfaccia Map.
di valori (mlue) e si usa quando si vogliono cercare oggetti (i valori) usando una chiave.
La Figura 10 mostra una mappa che associa nomi di persone al loro colore preferito.
746 C apitoloM
Così come ci sono due realizzazioni diverse di insieme, la libreria di Java fornisce due
implementazioni anche per Tinterfaccia Map: HashMap e TreeMap.
Dopo aver costruito un oggetto di tipo HashMap o TreeMap, dovreste memorizzarne il
riferimento in una variabile di tipo Map:
Map<String, Color> favoriteC olors = new HashMap<>();
Se chiedete informazioni su una chiave che non è associata ad alcun valore, il metodo
get restituisce nuli.
Per eliminare una chiave e il valore associato si invoca il metodo remove con la
chiave:
favoriteC olors. remove("l u l i e t ");
A volte capita di voler esaminare tutte le chiavi di una mappa, una dopo l’altra. 11 metodo
Per trovare tutte le chiavi e i valori presenti in
keySet restituisce un insieme contenente le chiavi, dopodiché potete chiedere un iteratore
a mappa, si scandisce l'insieme delle chiavi e si
erca il valore corrispondente a ciascuna chiave. a tale insieme e, da questo, ottenere tutte le chiavi, una dopo l’altra. Infine, per ogni chiave
si può invocare il metodo g et, ottenendo il valore associato. Quindi, le istruzioni seguenti
visualizzano tutte le coppie chiave/valore presenti nella mappa m:
Java C ollections F ramew/ork 747
}
scores. remove("Sally") ; E lim in a la ch iave e il su o valore.
File MapDemo.java
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Adam : java.awt.Color[r=255,g=0,b=0]
Eve : java.awt.Color[r=0,g=0,b=255]
I u lie t : java.awt.Color[r=0,g=0,b=255]
Romeo : java.awt.Color[r=0,g=255,b=0]
Agto-valutazione
16. Che differenza c’è tra un insieme e una mappa?
17. Perché la raccolta delle chiavi di una mappa è un insieme e non una lista?
18. Perché la raccolta dei valori di una mappa non è un insieme?
19. Supponete di voler tenere traccia del numero di ripetizioni di ciascuna parola all’interno
di un documento. Dichiarate una variabile con il tipo di mappa adatta.
20. Che tipo di oggetto è un Map<String, HashSet<String>>? Descrivete un possibile utilizzo
di una tale struttura.
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R 14.20, E 14.4 e E 14.5, al termine
del capitolo.
NoteperJavaS 14.1
Aggiornare le associazioni in una mappa
Le mappe vengono spesso utilizzate per contare le ripetizioni di vari dati. Ad esempio,
la sezione Esempi completi 14.1 usa un oggetto di tipo Map<String, Integer> per tenere
traccia del numero di ripetizioni di ciascuna parola in un file.
La gestione del caso speciale relativo all’inserimento del primo valore di ciascuna
coppia è un po’noiosa. Consideriamo il frammento di codice seguente, preso dalla sezione
Esempi completi 14.1:
Java 8 ha aggiunto all’interfaccia Map l’utile metodo merge, al quale occorre specificare:
• una chiave;
• un valore da usare se la chiave non è ancora presente;
• una funzione per calcolare il valore aggiornato se la chiave è già presente.
Java C ollections F ramework 749
La funzione va specificata sotto forma di espressione lambda (si veda la sezione Note per
Java 8 10.4). Ad esempio, l’enunciato seguente
compie le medesime azioni delle quattro righe di codice precedenti. Se la stringa word non
è presente nella mappa, vi viene inserita, associata al valore 1, altrimenti il suo vecchio
valore (oldValue) viene incrementato di un’unità.
Il metodo è utile anche quando i valori della mappa sono insiemi o stringhe con
componenti separate da virgole, come negli Esercizi E 14.6 e E 14.7.
• Gli elementi o le chiavi devono essere in ordine: usate TreeSet o TreeMap e passate
alla Fase 6.
• Gli elementi devono essere nello stesso ordine in cui sono stati inseriti: la scelta si
restringe a LinkedList o ArrayList.
• Non importa. A condizione di poter visitare tutti gli elementi, non vi importa in
quale ordine questo avviene. Se nella Fase 1 avete scelto di usare una mappa, usate
HashMap e passate alla Fase 5.
Fase 5. Per insiemi e mappe con tabella hash, decidete se occorre realizzare i metodi equals e
hashCode
6 EsemiMcompleti 14.1
Determinare la frequenza dì parole in un testo
Problema. Scrivere un programma che legge un file di testo e visualizza un elenco
contenente, in ordine alfabetico, tutte le parole presenti nel file, seguite da un conteggio
che indica il numero di ripetizioni di ciascuna parola.
Ecco, ad esempio, la parte iniziale del risultato prodotto dall’elaborazione del libro
“Alice in Wonderland” {Alice nel paese delle meraviglie):
a 653
abide I
able I
about 97
above 4
absence I
absurd 2
Fase 5. Per insiemi e mappe con tabella hash, decidete se occorre realizzare i metodi equals e
hashCode
File WordFrequency.java
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.io.File;
import java.io.FileNotFoundException;
/**
Visualizza il numero di ripetizioni di tutte le parole in "Alice in Wonderland"
*/
public class WordFrequency
{
public static void main(String[] args)
throws FileNotFoundException
{
MapcString, Integer> frequencies = new TreeMap<>();
Scanner in = new Scanner(new File("alice30.txt"));
while (in.hasNextO)
{
String word = clean(in.next());
frequencies.put(word, count);
r = r + c;
}
return r.toLowerCase();
int h = x.hashCodeO;
Tabella 6
bene il suo compito, generando quasi sempre numeri interi diversi in corrispondenza di
stringhe diverse. La Tabella 6 mostra alcune stringhe e i loro codici di hash.
Può succedere che due o più oggetti diversi abbiano lo stesso codice di hash, dando luogo
Una buona funzione di hash minimizza
le collisioni, che avvengono quando
a una collisione. Ad esempio, per le stringhe "VII” e "Ugh" viene calcolato lo stesso codice
a oggetti diversi vengono associati
di hash, ma si tratta di un evento molto raro (si veda l’Esercizio P l 4.5).
codici di hash identici. 11 metodo hashCode della classe String combina i caratteri della stringa che elabora,
generando un codice numerico che non è semplicemente la somma dei valori dei
singoli caratteri, perché questo non mescolerebbe a sufficienza i valori stessi: stringhe
che sono permutazioni di un’altra (come "eat" e "tea") avrebbero lo stesso codice di
hash.
754 C apitolo 14
Questo è il metodo usato nella libreria standard per calcolare il codice di hash di una
stringa:
Ad esempio, usando ovviamente i valori dei caratteri definiti dal codice Unicode, il
codice di hash di "eat" è:
Nelle vostre classi dovreste definire un codice di hash che combini in modo analogo i
Nelle vostre classi, sovrascrivete
il metodo hashCode combinando
codici di hash delle variabili di esemplare. Ad esempio, definiamo il metodo hashCode per
i codici di hash delie variabili
la classe Country vista nel Paragrafo 10.1.
di esemplare. La classe ha due variabili di esemplare: il nome della nazione e la sua superficie. Per
prima cosa calcoliamo i loro codici di hash: sapete già come calcolare il codice di hash
di una stringa; per calcolare il codice di hash di un numero in virgola mobile, costruite
dapprima un involucro di tipo Double che lo contenga, poi calcolate il suo codice di
hash.
public c la ss Country
{
public in t hashCodeO
{
in t hi = name.hashCodeO;
in t h2 = new Double(area).hashCode();
}
}
In ogni caso, è più semplice usare il metodo O bjects.hash, che calcola i codici di hash di
tutti i parametri ricevuti e li combina usando un moltiplicatore:
public in t hashCodeO
{
return 0bjects.hash(name, area);
}
Ja v a C ollections Fr a m e w o r k 755
Quando in una vostra classe definite il metodo hashCode, dovete definire anche un metodo
equals compatibile, perché questo viene utilizzato per distinguere tra loro due oggetti che
hanno lo stesso codice di hash.
1metodi hashCode e equals devono essere reciprocamente compatibili: due oggetti uguali
14.5.1 Pile
Una pila (stack) consente l’inserimento e la rimozione di elementi a una sola estremità,
Una pila è una raccolta di elementi
con modalità di rimozione
che viene tradizionalmente chiamata cima (top) della pila. Nuovi oggetti vengono inseriti
"last-in, first-out"
in cima alla pila e gli oggetti possono essere rimossi soltanto dalla cima della pila. Di
conseguenza, gli oggetti vengono rimossi in ordine inverso rispetto a come sono stati
inseriti, cioè “l’ultimo inserito è il primo a essere estratto” {'dast-in, first-ouf \ modalità
LIFO). Ad esempio, se inserite gli elementi A, B e C in questo ordine e poi li estraete,
otterrete, nell’ordine, C, B e A. Le operazioni di inserimento e di rimozione vengono
tradizionalmente chiamate push e pop.
Ci sono moki possibili utilizzi per le pile nell’informatica: pensate alla possibilità, negli
elaboratori di testo, di annullare !’ultima operazione eseguita (“annulla” o “undo”), anche
ripetutamente, procedendo a ritroso. Per consentire questo, il programma memorizza
in una pila i comandi che vengono eseguiti. Quando richiedete un’operazione di
annullamento, viene annullato Vultirno comando eseguito, poi il penultimo, e così via.
Un altro esempio significativo è la pila di esecuzione {run-time stack) gestita da un
processore o da una macchina virtuale per memorizzare le variabili di metodi annidati:
ogni volta che viene invocato un metodo, si inseriscono in cima a una pila i suoi parametri
e le sue variabili locali; quando il metodo termina, si estraggono dalla pila parametri e
variabili.
Nel Paragrafo 14.6 vedrete altre possibili applicazioni.
Nella libreria di Java è presente una semplice classe Stack dotata dei metodi push, pop e
peek, !’ultimo dei quali restituisce l’elemento che si trova in cima alla pila senza eliminarlo
dalla struttura (come riportato nella Tabella 7).
14.5.2 Code
Una coda (queue) consente l’inserimento di elementi a un’estremità della coda (fine della
Una coda è una raccolta dì elementi
con modalità di rimozione
coda, tail) e la rimozione all’altra estremità (inizio della coda, head). Gli oggetti vengono
"first-in, first-out".
rimossi seguendo la modalità così descritta:“il primo inserito è il primo a essere estratto”
C'first-in, first-ouV\ modalità FIFO). Gli elementi vengono, quindi, rimossi nello stesso
ordine in cui sono stati inseriti.
Una tipica applicazione è la coda di stampa. Una stampante può essere disponibile per
diverse applicazioni, eventualmente eseguite su diversi calcolatori:se ciascuna applicazione
tentasse di accedere alla stampante nello stesso momento, la stampa risultante sarebbe
una totale confusione; al contrario, ogni applicazione inserisce in un file tutti i byte che
deve inviare alla stampante e aggiunge tale file alla coda di stampa. Quando la stampante
ha terminato la stampa di un file estrae il successivo dalla coda, per cui i lavori vengono
stampati secondo la regola “il primo entrato è il primo a uscire”, che è una strategia
accettabile per gli utenti della stampante condivisa.
Come riportato nella Tabella 8, l’interfaccia Queue della libreria standard di Java ha i
metodi add per inserire un elemento alla fine della coda, remove per eliminare l’elemento
che si trova all’inizio della coda e peek per ispezionare tale elemento senza eliminarlo.
La classe LinkedList implementa l’interfaccia Queue, per cui, quando vi serve una coda,
potete semplicemente assegnare un oggetto di tipo LinkedList a una variabile di tipo Queue:
Ja v a C ollections Fr a m e w o r k 757
Quando si invoca q.remove() per la prima volta viene eliminata la richiesta che ha priorità
1. La successiva invocazione di q.remove() elimina la richiesta che ha la priorità più elevata
tra quelle rimaste: nel nostro esempio, la priorità di valore minimo (cioè la più elevata)
è 2. Se sono presenti due elementi che hanno la stessa priorità, la coda prioritaria può
risolvere la situazione di parità in modo arbitrario.
Dato che la coda prioritaria deve essere in grado di stabilire quale elemento ha la
priorità minima, gli elementi inseriti nella raccolta devono essere esemplari di una classe
che implementa l’interfaccia Comparable (descritta nel Paragrafo 10.3).
758 C apitolo 14
LaTabella 9 mostra i metodi principali della classe PriorityOueue presente nella libreria
standard.
Tabella 9 PriorityQueue<Integer> q = Questa coda prioritaria contiene oggetti di tipo Integer. Di solito,
Lavorare con code new PriorityOueueo 0 ; invece, si usano oggetti che descrivono compiti da svolgere.
prioritarie
q.add(3); q.add(l); q.add(2); Aggiunta di elementi alla coda prioritaria.
int first = q.removeO; Ogni invocazione di remove elimina l’elemento più urgente: a
int second = q.remove(); first viene assegnato il valore 1, a second il valore 2.
int next = q.peek(); Ispezione dell’elemento con priorità di valore minimo tra quelli
della coda prioritaria, senza eliminarlo.
Auto-valutazione
21. Perché, invece di dichiararla semplicemente come lista concatenata, si dichiara una
variabile in questo modo?
Oueue<String> q = new LinkedList<>();
22. Per quale motivo un vettore non è adatto all’implementazione di una coda?
23. Cosa visualizza questo frammento di codice?
Oueue<String> q = new LinkedList<>();
q.add("A");
q.add("B");
q.addC'C");
while (q .siz e O > O) { System .ou t.p r in t(q .remove() + " "); }
24. Per quale motivo una pila non è adatta alla gestione dei file inviati a una stampante?
25. Nel codice visto come esempio di utilizzo di una coda prioritaria abbiamo usato la classe
WorkOrder: avremmo potuto invece usare stringhe, in questo modo?
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R 14.15, E 14.8 e E 14.9, al termine
del capitolo.
scrivendo gli operatori dopo i relativi operandi, introducendo quella che venne chiamata
notazione polacca inversa (RPN, reverse Polish notation).
N o t a z i o n e standard N o t a z i o n e p o la c c a inversa ( R P N )
3 + 4 3 4 +
3 + 4x5 3 4 5 x +
3 X (4 + 5) 3 4 5 + X
(3 + 4) X (5 + 6) 3 4 + 5 6 + X
3 + 4 + 5 3 4 + 5 +
La notazione RPN può sembrare strana ma si tratta solo di un caso, dovuto agli avveni
menti storici: se i matematici ne avessero compreso i vantaggi fin dall’inizio, gli studenti
di oggi la userebbero con naturalezza e non si preoccuperebbero più di parentesi e regole
di precedenza.
Nel 1972 Hewlett-Packard presentò la calcolatrice HP 35, che usava la notazione
polacca inversa o RPN (Reverse Polish Notation): non aveva tasti per le parentesi o per
il segno di uguale, ma si usava il tasto ENTER (“Invio”) per inserire un numero sulla
pila, perciò la divisione marketing di Hewlett-Packard aveva creato per tale prodotto lo
slogan “la calcolatrice che non ha uguale”, giocando sul doppio senso della frase.
Con il passare del tempo gli sviluppatori di calcolatrici hanno deciso di adottare la nor
male notazione algebrica, piuttosto che costringere gli utilizzatori a imparare una nuova
notazione, ma quegli utenti che avevano fatto lo sforzo di imparare RPN tendono a
esserne fanatici sostenitori e anche oggi alcuni modelli di Hewlett-Packard gestiscono
tale notazione.
- { l b - b - { 4 - a - c ) ] / (2 a) }
Per vedere se una tale espressione è composta in modo corretto, si inseriscono le sue
parentesi in una pila:
Ecco un’esecuzione dell’algoritmo passo dopo passo, nell’analisi della semplice espressione
precedente:
Se, però, si scrivono gli operatori dopo i loro operandi si possono eliminare le parentesi
e l’espressione precedente diventa 3 4 + 5 x (come visto nella sezione Argomenti avanzati
14.2). Per valutare questa espressione bisogna applicare l’operatore di addizione, +, a 3 e 4,
ottenendo 7,per poi valutare l’espressione 7 5 x,che genera il risultato 35. Per espressioni
più complesse, il problema si complica. Ad esempio, 3 4 5 + x richiede il calcolo di 4 5 +
(il cui risultato è 9), per poi valutare l’espressione 3 9 x. Se valutiamo questa espressione
da sinistra a destra, come si fa normalmente, dobbiamo lasciare in sospeso l’operando 3
da qualche parte mentre valutiamo la sotto-espressione 4 5 +.Dove lo parcheggiamo? Lo
mettiamo in una pila. In effetti, l’algoritmo che valuta espressioni in notazione polacca
inversa è piuttosto semplice:
File Calculator.java
public c la ss Calculator
{
public s ta tic void main(String[] args)
{
Scanner in = new Scanner(System.in);
Stack<Integer> r e su lts = new Stack<>();
System.out.printIn("Enter one number or operator per lin e , Q to q u it. ");
762 C apitolo 14
i f (input.equals("+"))
{
r e s u lts. pu sh (resu lts.pop () + r esu lts.p o p O );
}
e ls e i f (in pu t.equ als("-"))
{
Integer arg2 = resu lts.p op O ;
r e s u lt s .pu sh (resu lts.pop () - arg2);
}
e ls e i f (input.equals("*") || input.equals(''x"))
{
r e s u lt s .push(results.pop() * r esu lts.p o p O );
}
e ls e i f (in p u t.eq u als(" /" ))
{
Integer arg2 = resu lts.p op O ;
r e s u lt s .push(results.pop() / arg2);
}
e ls e i f (input.equals("0") || input.equals("q"))
{
done = true;
}
e ls e
{
/ / non è un operatore, impila i l valore
O + 4
O 4
Il risultato è 7
O X 4 +5
O 4+5
O + 5 Valuta X prima di +
I I
Dato che l’operatore X ha la precedenza sull’operatore +,siamo pronti a valutare la cima:
12
17 Q uesto è il risultato
Espressione Commenti
Pila operandi Pila operatori da elaborare
Vuota Vuota 3 + 4 x5
O + 4 x5
e 4+5
764 C apitolo 14
O x5 N o n valutare x adesso
In altre parole, lasciamo gli operatori nella relativa pila fino a quando non siamo pronti
a valutarli. Ecco la parte rimanente della valutazione:
O X (4 + 5)
O (4 + 5)
O 4 + 5) N o n valutare x adesso
+ 5)
Valuta la cima
della pila
Ja v a C ollections Fr a m e w o r k 765
Ecco l’algoritmo:
Al termine, il valore rimasto in cima alla pila degli operandi è il valore dell’espressione.
L’algoritmo utilizza il seguente metodo ausiliario, che valuta l’operatore che si trova
in cima alla pila degli operatori usando come operandi i due che si trovano in cima alla
pila degli operandi:
Valuta la cima:
Estrai due operandi dalla pila degli operandi.
Estrai un operatore dalla pila degli operatori.
Esegui l'operazione tra l'operatore e gli operandi.
Inserisci il risultato nella pila degli operandi.
14.6.4 Backtracking
Immaginate di essere all’interno di un labirinto e di dover trovare l’uscita. Cosa dovete
Per poter tornare sui propri passi
e prendere una strada diversa,
fare quando vi trovate in un incrocio? Potete proseguire esplorando uno dei percorsi
un algoritmo di backtracking usa
che si diramano, ma volete ricordare i percorsi alternativi: se il percorso scelto non è
una pila per ricordare le alternative quello giusto, potrete tornare indietro e tentare una delle alternative che non avete
ancora inesplorate. ancora provato.
Ovviamente, seguendo il percorso potrete trovare altri incroci, le cui alternative
vanno analogamente memorizzate. Una soluzione semplice consiste nell’utilizzare una
pila, in cui inserire i percorsi che devono ancora essere esplorati. La procedura che prevede
di tornare indietro a un punto in cui si è effettuata una scelta per provare una diversa
7bb LAPITOLO 14
alternativa si chiama backtracking (“procedere a ritroso”). Usando una pila tornerete alla
scelta compiuta più di recente prima di ritornare a quelle più lontane nel passato.
La Figura 11 mostra un esempio. Partiamo da un punto all’interno del labirinto, la
posizione (3,4). Ci sono quattro percorsi possibili e li inseriamo tutti in una pila (riquadro
1) indicando con una freccia la direzione da prendere a partire dal punto memorizzato.
Estraiamo l’indicazione che si trova in cima alla pila e ci spostiamo verso nord a partire
dalla posizione (3, 4), arrivando nella posizione (1, 4). A questo punto inseriamo due
alternative nella pila, indecisi se proseguire verso ovest o verso est (riquadro 2). Entrambe
le strade, però, portano a un vicolo cieco (riquadri 3 e 4).
Ora estraiamo dalla pila l’indicazione di procedere verso est a partire dalla posizione
(3, 4): questo ci porta di nuovo in un vicolo cieco (riquadro 5). Il prossimo tentativo
è il percorso che da (3, 4) si dirige verso sud. Nel punto (5, 4) troviamo un incrocio e
inseriamo nella pila le due alternative possibili (riquadro 6): portano entrambe in un
vicolo cieco (riquadri 7 e 8).
Figura 1 ^ 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
Uscireda un labirinto 34? 54 4
usando il backtracking 1 ■ ■ 34^ 54^
2 ■■■■ ■■■
■ ■ 3 A Ì 34<
4 ■■■■ ■■■
5 ■ ■■■ 34 f -
6 ■■■■ ■■■
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
1 4-> 54<
1 ■ m a
1 A<r- 34<
2 BBBBaaaa
3 B B 34^
4 B B B B BB B
5 B BBB 34 4
6 B B B B BB B 34 < -
0 1 2 3 4 5 6 7
O 0 12 3 4 5 6 7 I
I■ ■■■■
14 ^
1 B ■ I 34^ 1
2 B B B B BB B 34 - > 2 BBBB BB B
B 34 4 3 B
4 B B B B BB B 4 BBBB BB B
5 B BB B 3 4<- SB BBB
6 B B B B BB B 6 BBBB BB B
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
34^
1 BBBBB B
2 B B B B BB B 34 4
B 3A < r-
4 B B B B BB B
5 B BB B
6 B B B B BB B
0 1 2 3 4 5 6 7
3 À J
2 SSSSSnSB
BBBB
34<-
4 B B B B BB B
5 B BB B
6 B B B B BB B
JAVA ^COLLECTIONS TRAMEWORK /O /
Infine, il percorso che da (3, 4) va verso ovest ci porta all’uscita (riquadro 9).
Abbiamo trovato un percorso per uscire dal labirinto usando una pila. Ecco, infatti,
lo pseudocodice che descrive l’algoritmo di uscita da un labirinto:
Se il labirinto non ha percorsi ciclici^ questo algoritmo troverà un’uscita. Se, invece, è
possibile che si segua un percorso ciclico e si torni a un incrocio già visitato seguendo una
sequenza di percorsi diversi, allora per trovare un’uscita bisogna lavorare più duramente,
come delineato nell’Esercizio E 14.21.
L’implementazione di questo algoritmo è strettamente correlata al modo in cui
si descrive il labirinto. Ad esempio, questo si può fare con un array bidimensionale di
caratteri, con spazi che rappresentano i corridoi di passaggio e asterischi che indicano le
barriere invalicabili:
t*** ***
♦
♦
:(c]|c>4c4c
Per rappresentare una posizione di partenza e una direzione (nord, est, sud o ovest) si può
progettare una classe Path, mentre la classe Maze avrà un metodo che estende un percorso
lungo la direzione attuale fino a raggiungere un incrocio o un’uscita, oppure finché non
viene bloccato da una barriera, oltre a un metodo che individua tutti i percorsi che si
diramano da un incrocio.
Si noti che in questo algoritmo si può usare una coda al posto della pila: di conseguenza,
verranno esplorate le alternative meno recenti prima di quelle più recenti. Per trovare
una soluzione del problema questo approccio è altrettanto efficace, ma, pensando
all’esplorazione concreta di un labirinto, è decisamente meno intuitivo: bisogna immaginare
di poter essere teletrasportati indietro all’incrocio iniziale invece di camminare a ritroso
fino all’iiltimo incrocio visitato.
Auto-valutazione
26. Qual è il valore dell’espressione 2 3 4 + 5 x x i n notazione polacca inversa?
27. Perché nel programma C alculator la gestione dell’operatore di sottrazione non esegue
semplicemente l’enunciato seguente?
768 C apitolo 14
r e s u lt s .push(results.pop() - r e s u lt s .pop());
28. Valutando l’espressione 3 - 4 + 5 con l’algoritmo visto nel Paragrafo 14.6.3, quale
operatore viene valutato per primo?
29. NeH’algoritmo visto nel Paragrafo 14.6.3 gli operatori, all’interno della loro pila, sono
sempre disposti in ordine di precedenza crescente?
30. Considerate il seguente esempio di labirinto. Immaginando di partire dal punto indicato e
inserendo nella pila i percorsi alternativi nell’ordine ovest, sud, est e nord, in quale ordine
vengono visitati i punti contrassegnati dalle lettere eseguendo l’algoritmo presentato nel
Paragrafo 14.6.4?
IA BH c DI
■■■■■ I
Ie f Gl
■■ ■■ I
I IH j Kl
Im I
I NI
Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R 14.25, E 14.18, El 4.20, E l4.21 e
E 14.22, al termine del capitolo.
Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
R 1 4 .6 .
concatenata dopo ciascun passo.
LinkedList<String> sta ff = new LinkedList<>();
s t a f f . addFirst( "Harry");
staff.addFirst("Diana");
s t a f f . addFirst( "Tom");
System. out. println(s t a f f . removeLast( ) ) ;
System. out. println(s t a f f . removeFirst());
System. out. println(s t a f f . removeLast( ) ) ;
Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
R 1 4 .7 .
concatenata dopo ciascun passo.
LinkedList<String> sta ff = new LinkedListo();
s t a f f . addFirst( "Harry");
staff.addLast("Diana");
staff.addFirst("Tom");
System. out. println(s t a f f . removeLast( ) ) ;
System. out. println(s t a f f . removeFirst());
System. out. println( s t a f f . removeLast( ) ) ;
Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
R 1 4 .8 .
concatenata e la posizione dell’iteratore dopo ciascun passo.
LinkedList<String> sta ff = new LinkedList<>();
ListIterator<String> iterator = s t a f f . l i s t Iterator();
iterator. add( "Tom") ;
iterator. add( "Diana") ;
iterator.add("Harry");
Ja v a C ollections Fr a m e w o r k 771
* R14.9. Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
concatenata e la posizione dell’iteratore dopo ciascun passo.
LinkedList<String> sta ff = new LinkedList<>();
ListIterator<String> iterator = s ta ff.lis tI te r a to r ();
iterator. add( "Tom") ;
iterator. add( "Diana") ;
iterator. add( "Harry") ;
iterator = s ta ff.listI te r a to r ();
iterator.nextO ;
iterator.nextO ;
iterator. add( "Romeo") ;
iterator.nextO ;
iterator. add( "Du lie t") ;
iterator = s ta ff.listI te r a to r ();
iterator.nextO ;
iterator. removeO;
while (iterator.hasNextO ) { System.out.print In (iterator.nextO ); }
R14.10. Data una lista concatenata di stringhe, come vi si eliminano tutti gli elementi che hanno
lunghezza non superiore a tre?
R14.il (per Java 8). Risolvete nuovamente l’esercizio precedente usando il metodo removelf,
dopo averne letto la descrizione nella documentazione API dell’interfaccia Collection. Usate
un’espressione lambda (descritta nella sezione Note per Java 8 10.4).
R14.12. Quali vantaggi e svantaggi hanno le liste concatenate rispetto agli array?
R14.13. Supponete di dover organizzare un elenco di numeri di telefono per un reparto di una
società. Al momento vi sono circa 6000 dipendenti, sapete che il centralino può gestire al massimo
10000 numeri di telefono e prevedete che l’elenco venga consultato diverse centinaia di volte al
giorno. Per memorizzare le informazioni usereste un vettore o una lista concatenata?
R14.14. Immaginate di dover gestire un elenco di appuntamenti. Usereste una lista concatenata
O un vettore di oggetti di tipo Appointment?
* R14.15. Supponete di scrivere un programma che simula un mazzo di carte. Le carte vengono
pescate dalla cima del mazzo e distribuite ai giocatori. Quando le carte tornano nel mazzo, vengono
poste al di sotto del mazzo stesso. Memorizzereste le carte in una pila o in una coda?
^ R14.16. Ipotizzate che le stringhe "A" ... "Z" vengano inserite in una pila. Successivamente,
vengono estratte dalla pila e inserite in una seconda pila. Infine, vengono estratte dalla seconda
pila e visualizzate. In quale ordine?
* R14.17. Qual è la differenza fra un insieme e una mappa?
R14.18. L’unione di due insiemi ^4 e B è l’insieme di tutti gli elementi che sono contenuti in A,
in B O in entrambi. L’intersezione è , invece, l’insieme di tutti gli elementi che sono contenuti sia
in A sia in B. Come si possono calcolare l’unione e l’intersezione di due insiemi, usando i metodi
add e contains, oltre a un iteratore?
R14.19. Come si possono calcolare l’unione e l’intersezione di due insiemi usando i metodi
forniti dall’interfaccia ja v a .u til.S e t, ma senza usare un iteratore? Consultate la documentazione
API dell’interfaccia nella libreria standard.
772 C apitolo 14
R l4.20. Una mappa può avere due chiavi associate allo stesso valore? E due valori associati alla
stessa chiave?
R14.21. Una mappa può essere realizzata mediante un insieme di coppie {chiave, valore). Date una
spiegazione.
R14.22 (per Java 8). Come si possono visualizzare tutte le coppie chiave/valore presenti in una
mappa usando il metodo keySet? E usando il metodo entrySet? E il metodo forEach fornendo
un’espressione lambda (descritta nella sezione Note per Java 8 10.4)?
R14.23. Verificate che il codice di hash della stringa "Duliet" sia quello riportato nella Tabella 6.
R14.24. Verificate che le stringhe "VII" e "Ugh" abbiano lo stesso codice di hash.
R14.25. Considerate l’algoritmo di uscita da un labirinto visto nel Paragrafo 14.6.4,immaginando
di partire dalla posizione A e di inserire nella pila le direzioni nell’ordine ovest, sud, est e nord. In
quale ordine vengono visitate le posizioni indicate dalle lettere nel labirinto qui rappresentato?
R14.26. Ripetete l’esercizio precedente, usando una coda invece di una pila.
Carl: B+
Ioe : C
Sarah: A
E14.5. Scrivete un programma che legga un file di codice sorgente Java e generi un elenco di
tutti gli identificatori presenti, visualizzando, accanto a ciascuno di essi, i numeri delle righe in
cui compare. Per semplicità considereremo che qualsiasi stringa costituita soltanto da lettere, cifre
numeriche e caratteri di sottolineatura sia un identificatore. Dichiarate la variabile Scanner in per
leggere il file e invocate il metodo in.useDeliiniter("[''A-Za-zO-9_]+"),in modo che ogni invocazione
di next restituisca un identificatore.
E14.6 (per Java 8). Leggete da un file tutte le parole presenti e aggiungetele a una mappa
le cui chiavi siano le lettere iniziali delle parole e i cui valori siano insiemi contenenti le
parole che iniziano con quella stessa lettera. Quindi, visualizzate gli insiemi di parole in ordine
alfabetico.
Risolvete l’esercizio in due modi, uno che usi il metodo merge (descritto nella sezione Note
per Java 8 14.1) e uno che aggiorni la mappa come nella sezione Esempi completi 14.1.
E14.7 (per Java 8). Leggete da un file tutte le parole presenti e aggiungetele a una mappa le cui
chiavi siano le lunghezze delle parole e i cui valori siano stringhe composte da parole separate
da virgole, con parole aventi tutte la stessa lunghezza. Quindi, visualizzate tali stringhe in ordine
crescente di lunghezza delle loro singole parole componenti.
Risolvete l’esercizio in due modi, uno che usi il metodo merge (descritto nella sezione Note
per Java 8 14.1) e uno che aggiorni la mappa come nella sezione Esempi completi 14.1.
E14.8. Usate una pila per invertire le parole di una frase. Continuate a leggere parole, aggiungendole
alla pila, fin quando non trovate una parola che termina con un punto. A questo punto estraete
tutte le parole dalla pila e visualizzatele, poi ripetete la procedura fino all’esaurimento dei dati in
ingresso. Ad esempio, questa frase
Mary had a l i t t l e lamb. Its fleece was white as snow
Fate attenzione alle lettere maiuscole e al posizionamento del punto che termina la frase.
E l4.9. Dovete scomporre nu numero intero nelle sue singole cifre, trasformando, ad esempio, il
numero 1729 nella sequenza di cifre 1,7, 2 e 9. L’ultima cifra del numero n si ottiene facilmente
calcolando n %io, ma procedendo in questo modo si ottengono le cifre in ordine inverso. Risolvete
il problema usando una pila. 11 programma deve chiedere all’utente di fornire un numero intero,
per poi visualizzarne le singole cifre separate da spazi.
E14.10. In occasione di manifestazioni particolari, il proprietario di una casa noleggia posti auto
nel suo vialetto di casa, che può essere rappresentato da una pila, con il consueto comportamento
“last-in, first-out”. Quando il proprietario di un’automobile se ne va e la sua automobile non è
l’ultima, tutte quelle che la bloccano devono essere spostate temporaneamente sulla strada, per poi
rientrare nel vialetto. Scrivete un programma che simuli questo comportamento, usando una pila per
il vialetto e una per la strada, con numeri interi a rappresentare le targhe delle automobili. Un numero
positivo inserisce un’automobile nel vialetto, un numero negativo la fa uscire definitivamente e il
numero zero termina la simulazione.Visualizzate il contenuto del vialetto al termine di ciascuna
operazione.
774 C apitolo 14
E l4.11. Dovete realizzare un “elenco di cose da fare” {to do lisi).A ciascun compito viene assegnata
una priorità, un numero intero da 1 a 9, e una descrizione. Quando l’utente digita il comando add
priorità descrizione il programma aggiunge una cosa da fare, mentre quando l’utente digita next
il programma elimina e visualizza la cosa da fare più urgentemente. 11 comando quit termina il
programma. Risolvete il problema usando una coda prioritaria.
E14.12. Scrivete un programma che legga un testo da un file e lo suddivida in singole parole. Inserite
le parole in un insieme realizzato mediante un albero. Dopo aver letto tutti i dati, visualizzate tutte
le parole, seguite dalla dimensione dell’insieme risultante. Questo programma determina, quindi,
quante parole diverse sono presenti in un testo.
E14.13. Leggendo tutte le parole di un file di testo di grandi dimensioni (come il romanzo “War
and Peace”, Guerra e d i s p o n i b i l e in Internet), inseritele in due insiemi, uno realizzato mediante
tabella hash e uno realizzato mediante albero. Misurate i tempi di esecuzione: quale struttura agisce
più velocemente?
E14.14. Realizzate, nella classe BankAccount del Capitolo 8, metodi hashCode e equals che siano fra
loro compatibili.Verificate la correttezza dell’implementazione del metodo hashCode visualizzando
codici di hash e aggiungendo oggetti BankAccount a un insieme realizzato con tabella hash.
E14.15. Un punto geometrico dotato di etichetta è caratterizzato dalle coordinate x e y, oltre
che dall’etichetta, sotto forma di stringa. Progettate la classe LabeledPoint dotata del costruttore
LabeledPoint(int x, in t y, String lab el) e dei metodi hashCode e equals: due punti sono considerati
uguali quando si trovano nella stessa posizione e hanno la stessa etichetta.
E14.16. Realizzate una diversa versione della classe LabeledPoint vista nell’esercizio precedente,
memorizzando la posizione del punto in un oggetto di tipo java.aw t.Point. I metodi hashCode e
equals devono invocare gli omonimi metodi della classe Point.
E14.17. Modificate la classe LabeledPoint dell’Esercizio E14.15 in modo che implementi l’interfaccia
Comparable. Fate in modo che i punti vengano ordinati innanzitutto in base alla loro coordinata
x; se due punti hanno la stessa coordinata x, ordinateli in base alla loro coordinata y; se due punti
hanno le stesse coordinate, ordinateli in base alla loro etichetta. Scrivete un programma di collaudo
che verifichi tutti i casi, inserendo punti in un TreeSet.
E14.18. Aggiungete al valutatore di espressioni visto nel Paragrafo 14.6.3 l’operatore %,che calcola
il resto della divisione intera.
E14.19. Aggiungete al valutatore di espressioni visto nel Paragrafo 14.6.3 l’operatore '',che effettua
l’elevamento a potenza. Ad esempio, 2 ^ 3 ha come risultato 8. Come in matematica, l’elevamento
a potenza deve essere valutato da destra verso sinistra, cioè 2 ^ 3 ^ 2 è uguale a 2 ^ (3 ^ 2) e non
a (2 ^ 3) ^ 2 (quest’ultima quantità si può calcolare come 2 ^ (3 x 2)).
E14.20. Scrivete un programma che verifichi se una sequenza di marcatori HTML è annidata
correttamente. Per ogni marcatore di apertura, come <p>, ci deve essere un marcatore di chiusura,
</p>. All’interno di una coppia di marcatori, come <p> . . . </p>, possono essere presenti altri
marcatori, come in questo esempio:
<p> <ul> <li> </li> </ul> <a> </a> </p>
I marcatori più interni deve essere racchiusi tra quelli più esterni. Il programma deve elaborare un
file contenente marcatori: per semplicità, ipotizzate che i marcatori siano separati da spazi e che
al loro interno non ci sia altro testo, ma solo altri marcatori.
Ja v a C ollections Fr am ew or k 775
E14.21. Modificate il solutore di labirinti visto nel Paragrafo 14.6.4 in modo che possa gestire
anche labirinti con cicli. Utilizzate un insieme di incroci visitati: quando arrivate in un incrocio
già visitato in precedenza, trattatelo come un vicolo cieco e non aggiungete alla pila le sue
alternative.
E14.22. In un programma per disegnare, l’operazione di “riempimento per inondazione’’{floodfili)
assegna un determinato colore a tutti i pixel vuoti di un disegno, fermandosi quando raggiunge pixel
occupati. In questo esercizio svilupperete una semplice variante di questo algoritmo, riempiendo
per inondazione un array di numeri interi di dimensione 10 x 10, inizialmente riempito di
zeri. Chiedete all’utente la posizione iniziale: riga e colonna. Inserite in una pila la coppia <riga,
colonna> (vi servirà una semplice classe Pair).
Quindi, ripetete queste operazioni finché la pila non è vuota.
• La coppia <riga, colonna> presente in cima alla pila viene estratta.
• Se la posizione corrispondente è ancora vuota, riempitela, usando via via i numeri 1, 2,3, ecc.,
in modo da visualizzare l’ordine di riempimento delle celle.
• Le coordinate delle celle adiacenti in direzione nord, est, sud e ovest, che non siano state riem
pite, vengono inserite nella pila.
In questo caso, E è una variabile di tipo, non una parola riservata di Java; invece di E po-
treste usare un nome diverso, come ElementType, ma per le variabili di tipo si è soliti usare
nomi brevi e composti di lettere maiuscole.
Per poter usare una classe generica, dovete fornire un tipo effettivo che sostituisca
I tipi parametrici possono essere
sostituiti, all’atto della creazione
il tipo parametrico; si può usare il nome di una classe oppure di un’interfaccia, come in
di esemplari, con nomi di classi questi esempi:
o di interfacce.
ArrayList<BankAccount>
ArrayList<Measurable>
Non si può, però, sostituire un tipo parametrico con uno degli otto tipi di dati primitivi,
quindi sarebbe un errore creare un oggetto di tipo ArrayList<double>: usate la corrispon-
dente classe involucro, ArrayList<Double>.
Quando create un esemplare di una classe generica, il tipo di dato che indicate va
a sostituire tutte le occorrenze della variabile di tipo utilizzata nella dichiarazione della
classe.Ad esempio, nel metodo add di un oggetto di tipo ArrayList<BankAccount>, la variabile
di tipo, E, viene sostituita dal tipo BankAccount:
public void add(BankAccount element)
Auto-valutazione
1. La libreria standard mette a disposizione la classe HashMap<K, V>, dove K è il tipo della
chiave e V è il tipo del valore. Come esemplare di tale classe, costruite una mappa che
memorizzi associazioni tra stringhe e numeri interi.
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
Questa classe può essere utile quando si realizza un metodo che calcola e deve restituire
due valori: un metodo non può restituire contemporaneamente un esemplare di String
e un esemplare di Integer, mentre può restituire un singolo oggetto di tipo Pair<String,
Integer>.
La classe generica Pair richiede due tipi parametrici, uno per il tipo del primo ele-
mento e uno per il tipo del secondo elemento.
Dobbiamo scegliere le variabili per questi tipi parametrici. Solitamente per le variabili
di tipo si usano nomi brevi e composti di sole lettere maiuscole, come in questi esempi:
Le variabili di tipo di una classe generica vanno indicate dopo il nome della classe, rac-
Le variabili di tipo di una classe
generica vanno indicate dopo
chiuse tra parentesi angolari:
il nome della classe e sono racchiuse public class Pair<T, S>
tra parentesi angolari.
Nelle dichiarazioni delle variabili di esemplare e dei metodi della classe Pair, usiamo la
variabile di tipo T per indicare il tipo del primo elemento e la variabile di tipo S per il
Per indicare i tipi generici
delle variabili di esemplare,
tipo del secondo elemento:
dei parametri dei metodi e dei valori public class Pair<T, S>
da essi restituiti, usate {
le variabili di tipo. private T first;
private S second;
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
Alcuni trovano più semplice partire dalla dichiarazione di una classe normale, scegliendo
tipi effettivi al posto delle variabili di tipo, come in questo esempio:
A questo punto è facile sostituire tutti i tipi String con la variabile di tipo S e tutti i tipi
Integer con la variabile di tipo T.
Ciò completa la definizione della classe generica Pair, che ora è pronta per essere
utilizzata ovunque abbiate bisogno di creare una coppia composta da due oggetti di tipo
qualsiasi. L’esempio che segue mostra come usare un oggetto di tipo Pair per progettare
un metodo che restituisca due valori.
File Pair.java
/**
Questa classe memorizza una coppia di elementi di tipi diversi.
*/
public class Pair<T, S>
{
private T first;
private S second;
/**
Costruisce una coppia contenente i due elementi ricevuti.
@param firstElement il primo elemento
@param secondElement il secondo elemento
*/
public Pair(T firstElement, S secondElement)
{
first = firstElement;
second = secondElement;
}
/**
Restituisce il primo elemento di questa coppia.
@return il primo elemento
*/
public T getFirst() { return first; }
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
/**
Restituisce il secondo elemento di questa coppia.
@return il secondo elemento
*/
public S getSecond() { return second; }
public String toString() { return "(" + first + ", " + second + ")"; }
}
File PairDemo.java
public class PairDemo
{
public static void main(String[] args)
{
String[] names = { "Tom", "Diana", "Harry" };
Pair<String, Integer> result = firstContaining(names, "a");
System.out.println(result.getFirst());
System.out.println("Expected: Diana");
System.out.println(result.getSecond());
System.out.println("Expected: 1");
}
/**
Restituisce la prima stringa contenente una stringa assegnata,
oltre al suo indice nell'array.
@param strings un array di stringhe
@param sub una stringa
@return una coppia (strings[i], i), dove strings[i] è la prima
stringa in strings contenente sub, oppure una coppia
(null, –1) se non si trovano corrispondenze
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
*/
public static Pair<String, Integer> firstContaining(
String[] strings, String sub)
{
for (int i = 0; i < strings.length; i++)
{
if (strings[i].contains(sub))
{
return new Pair<String, Integer>(strings[i], i);
}
}
return new Pair<String, Integer>(null, –1);
}
}
Auto-valutazione
2. Come usereste la classe generica Pair per costruire una coppia contenente le stringhe
"Hello" e "World"?
3. Che differenza c’è tra un oggetto di tipo ArrayList<Pair<String, Integer>> e uno di tipo
Pair<ArrayList<String>, Integer>?
Come detto nel paragrafo precedente, spesso è più facile capire come si realizza un metodo
generico partendo da un esempio concreto. Questo metodo visualizza tutti gli elementi
presenti in un array di stringhe.
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
Per rasformare tale metodo in un metodo generico, sostituite il tipo String con un tipo
I tipi parametrici di un metodo
generico vanno scritti tra
parametrico, diciamo E, che rappresenti il tipo degli elementi dell’array. Aggiungete un
i modificatori e il tipo del valore elenco di tipi parametrici, racchiuso tra parentesi angolari, tra i modificatori (in questo
restituito dal metodo. caso public e static) e il tipo del valore restituito (in questo caso void):
public static <E> void print(E[] a)
{
for (E e : a)
{
System.out.print(e + " ");
}
System.out.println();
}
Quando invocate il metodo generico, non dovete specificare i tipi effettivi da usare al
posto dei tipi parametrici (e in questo aspetto i metodi generici differiscono dalle classi
generiche): invocate semplicemente il metodo con i parametri appropriati e il compi-
latore metterà in corrispondenza i tipi parametrici con i tipi dei parametri. Ad esempio,
considerate questa invocazione di metodo:
Rectangle[] rectangles = ...;
ArrayUtil.print(rectangles);
Il tipo del parametro rectangles è Rectangle[], mentre il tipo della variabile parametro è
Quando invocate un metodo generico,
E[]: il compilatore ne deduce che il tipo effettivo da usare per E è Rectangle.
non dovete specificare esplicitamente
i tipi da usare al posto Questo particolare metodo generico è un metodo statico inserito in una classe
dei tipi parametrici. normale (non generica), ma potete definire anche metodi generici che non siano statici.
Potete, infine, definire metodi generici all’interno di classi generiche.
Come nel caso delle classi generiche, non potete usare tipi primitivi per sostituire
tipi parametrici. Il metodo generico print può, quindi, visualizzare array di qualsiasi tipo,
eccetto array di uno degli otto tipi primitivi. Ad esempio, non si può usare il metodo print
per visualizzare un array di tipo int[], ma questo non è un grande problema: realizzate
semplicemente, oltre al metodo generico print, un metodo print(int[] a).
Auto-valutazione
4. Cosa fa esattamente il metodo generico print quando fornite come parametro un array
di oggetti di tipo BankAccount contenente due conti bancari aventi saldo uguale a zero?
5. Il metodo getFirst della classe Pair è un metodo generico?
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
Potete invocare min con un array di tipo String[] ma non con un array di tipo Rectangle[]:
la classe String realizza Comparable, ma Rectangle no.
La limitazione al tipo Comparable è necessaria per poter invocare il metodo compareTo:
se non fosse stato specificato il vincolo, il metodo min non sarebbe stato compilato, perché
non sarebbe stato lecito invocare compareTo con l’oggetto a[i], del cui tipo non si avrebbe
avuto alcuna informazione (in realtà, la stessa interfaccia Comparable è un tipo generico, ma
per semplicità l’abbiamo utilizzata senza fornire un tipo come parametro; per maggiori
informazioni, consultate Argomenti avanzati 15.1).
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
Vi capiterà raramente di dover indicare due o più vincoli. In tal caso, separateli con
il carattere &, come in questo esempio:
<E extends Comparable & Cloneable>
La parola riservata extends, quando viene applicata ai tipi parametrici, significa in realtà
“estende o implementa”; i vincoli possono essere classi o interfacce e i tipi parametrici
possono essere sostituiti con il tipo effettivo di una classe o di un’interfaccia.
Auto-valutazione
6. Come vincolereste il tipo parametrico di una classe TreeSet generica?
7. Modificate il metodo min in modo che identifichi l’elemento minimo all’interno di un
array di elementi che realizzano l’interfaccia Measurable vista nel Capitolo 10.
ArrayList<SavingsAccount> savingsAccounts
= new ArrayList<SavingsAccount>();
// quanto segue non è lecito, ma supponiamo che lo sia
ArrayList<BankAccount> bankAccounts = savingsAccounts;
BankAccount harrysChecking = new CheckingAccount();
// CheckingAccount è una diversa sottoclasse di BankAccount
bankAccounts.add(harrysChecking);
// va bene, si possono aggiungere oggetti di tipo BankAccount
Un tipo specificato con carattere jolly è un tipo che può rimanere sconosciuto. Ad esem-
pio, nella classe LinkedList<E> si può definire il metodo seguente, che aggiunge alla fine
della lista concatenata tutti gli elementi contenuti in other.
public void addAll(LinkedList<? extends E> other)
{
ListIterator<E> iter = other.listIterator();
while (iter.hasNext()) { add(iter.next()); }
}
Il metodo addAll non richiede che il tipo degli elementi di other sia un qualche tipo
specifico: consente l’utilizzo di qualsiasi tipo che sia un sottotipo di E. Ad esempio, potete
usare addAll per aggiungere a un esemplare di LinkedList<BankAccount> tutti gli elementi
contenuti in un esemplare di LinkedList<SavingsAccount>.
Per vedere un tipo wildcard con vincolo di tipo super, torniamo al metodo min del
paragrafo precedente. Ricordate che Comparable è un’interfaccia generica e il tipo che la
rende generica serve a specificare il tipo del parametro del metodo compareTo.
public interface Comparable<T>
{
int compareTo(T other);
}
Di conseguenza, potremmo voler specificare un tipo vincolato:
public static <E extends Comparable<E>> E min(E[] a)
Questo vincolo, però, è troppo restrittivo. Immaginate che la classe BankAccount realizzi
l’interfaccia Comparable<BankAccount>: di conseguenza, anche la sua sottoclasse Savings-
Account realizza Comparable<BankAccount> e non Comparable<SavingsAccount>. Se volete usare il
metodo min con un array di tipo SavingsAccount[], allora il tipo che specifica il parametro
dell’interfaccia Comparable deve essere qualsiasi supertipo del tipo di elemento dell’array:
public static <E extends Comparable<? super E>> E min(E[] a)
Ecco, invece, un esempio di carattere jolly che specifica l’assenza di vincoli. La classe
Collections definisce il metodo seguente:
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
Come potete vedere, i tipi parametrici, T e S, sono stati sostituiti da Object; il risultato è
una classe ordinaria.
Ai metodi generici viene applicato il medesimo procedimento. Dopo la cancellazione
dei tipi parametrici, il metodo min del paragrafo precedente si trasforma in un metodo
ordinario; notate come, in questo esempio, il tipo parametrico sia sostituito dal suo vin-
colo, l’interfaccia Comparable.
public static Comparable min(Comparable[] a)
{
Comparable smallest = a[0];
for (int i = 1; i < a.length; i++)
{
if (a[i].compareTo(smallest) < 0) { smallest = a[i]; }
}
return smallest;
}
Conoscere l’esistenza dei tipi grezzi aiuta a capire i limiti della programmazione gene-
Non si possono costruire oggetti
o array di un tipo generico..
rica in Java. Ad esempio, non potete costruire esemplari di un tipo generico. Il seguente
metodo, che tenta di riempire un array con copie di oggetti predefiniti, sarebbe sbagliato:
public static <E> void fillWithDefaults(E[] a)
{
for (int i = 0; i < a.length; i++)
{
a[i] = new E(); // ERRORE
}
}
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
Per capire per quale motivo ciò costituisca un problema, eseguite il procedimento di
cancellazione dei tipi parametrici, come se foste il compilatore:
public static void fillWithDefaults(Object[] a)
{
for (int i = 0; i < a.length; i++)
{
a[i] = new Object(); // inutile
}
}
Ovviamente, se costruite un array di tipo Rectangle[], non volete che il metodo lo riempia
di esemplari di Object, ma, dopo la cancellazione dei tipi parametrici, questo è ciò che
farebbe il codice che abbiamo scritto.
In situazioni come questa, il compilatore segnala un errore, per cui dovete necessa-
riamente trovare un modo diverso per risolvere il vostro problema. In questo particolare
esempio, potete fornire uno specifico oggetto predefinito:
public static <E> void fillWithDefaults(E[] a, E defaultValue)
{
for (int i = 0; i < a.length; i++)
{
a[i] = defaultValue;
}
}
Dato che l’espressione che costruisce l’array, new E[], diventerebbe, dopo la cancellazione
dei tipi parametrici, new Object[], il compilatore non la consente. Come soluzione, si può
usare un vettore:
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
oppure si può usare un array di Object, inserendo un cast ogni volta che si legge un valore
contenuto nell’array:
public class Stack<E>
{
private Object[] elements;
private int size;
...
public Stack()
{
elements = new Object[MAX_SIZE]; // anche così va bene
}
...
public E pop()
{
size––;
return (E) elements[size];
}
}
Auto-valutazione
8. Cosa si ottiene applicando la cancellazione dei tipi parametrici al metodo print visto nel
Paragrafo 15.3?
9. Si potrebbe realizzare una pila in questo modo?
public class Stack<E>
{
private E[] elements;
...
public Stack()
{
elements = (E[]) new Object[MAX_SIZE];
}
}
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
private static class Node { public E data; public Node next; } // ERRORE
}
Nel caso di variabili statiche, questa limitazione è molto stringente. Dopo la cancellazione
dei tipi generici, esiste un’unica variabile, LinkedList.defaultValue, mentre la dichiarazione
della variabile statica lascerebbe falsamente intendere che ne esista una diversa per ogni
diverso tipo di LinkedList<E>.
Per i metodi statici e le classi interne statiche esiste una semplice alternativa: aggiun-
gere un tipo parametrico.
public class LinkedList<E>
{
...
public static <T> List<T> replicate(T value, int n) {...} // va bene
private static class Node<T> { public T data; public Node<T> next; }
// va bene
}
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
* R15.2. Qual è la differenza tra una classe generica e una classe ordinaria?
* R15.3. Qual è la differenza tra una classe generica e un metodo generico?
* R15.4. Nella libreria standard di Java, identificate un esempio di metodo generico non statico.
** R15.5. Nella libreria standard di Java, identificate quattro esempi di classi generiche che abbiano
due tipi parametrici.
** R15.6. Nella libreria standard di Java, identificate un esempio di classe generica che non sia una
delle classi che realizzano contenitori.
* R15.7. Perché nel metodo seguente serve un vincolo per il tipo parametrico, T?
<T extends Comparable> int binarySearch(T[] a, T key)
** R15.8. Perché nella classe HashSet<E> non serve un vincolo per il tipo parametrico, E?
* R15.9. Che cosa rappresenta un esemplare di ArrayList<Pair<T, T>>?
** R15.10. Illustrate i vincoli applicati ai tipi nel seguente metodo della classe collections:
Perché non è sufficiente scrivere <T extends Comparable> oppure <T extends Comparable<T>>?
Esercizi di programmazione
* E15.1. Modificate la classe generica Pair in modo che i due valori siano dello stesso tipo.
* E15.2. Aggiungete alla classe Pair dell’esercizio precedente un metodo swap che scambi tra loro
il primo e il secondo elemento della coppia.
** E15.3. Realizzate un metodo statico generico PairUtil.swap, il cui parametro sia un oggetto di
tipo Pair, usando la classe generica definita nel Paragrafo 15.2. Il metodo deve restituire una nuova
coppia avente il primo ed il secondo elemento scambiati rispetto alla coppia originaria.
** E15.4. Scrivete un metodo statico generico PairUtil.minmax che identifichi gli elementi minimo
e massimo presenti in un array di tipo T e restituisca una coppia contenente tali valori minimo e
massimo. Esprimete il fatto che gli elementi dell’array devono implementare l’interfaccia Measurable
vista nel Capitolo 10.
** E15.5. Risolvete nuovamente il problema dell’esercizio precedente, richiedendo però che gli
elementi dell’array implementino l’interfaccia Comparable.
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
*** E15.6. Risolvete nuovamente il problema dell’Eesercizio E15.4, richiedendo però che il tipo
parametrico estenda il tipo generico Comparable.
** E15.7. Realizzate una versione generica dell’algoritmo di ricerca binaria.
*** E15.8. Realizzate una versione generica dell’algoritmo di ordinamento per fusione. Il programma
deve essere compilato senza che vengano segnalati warning.
*** E15.9. Dotate di un appropriato metodo hashCode la classe Pair vista nel Paragrafo 15.2 e realizzate
una classe HashMap che usi un esemplare di HashSet<Pair<K, V>>.
*** E15.10. Realizzate una versione generica del generatore di permutazioni visto nel Paragrafo 12.4,
in modo che generi tutte le permutazioni degli elementi presenti in un esemplare di List<E>.
** E15.11. Scrivete un metodo statico generico, print, che visualizzi l’elenco degli elementi pre-
senti in qualsiasi esemplare di una classe che implementi l’interfaccia Iterable<E>, inserendolo in
un’appropriata classe di utilità. Gli elementi visualizzati devono essere separati da una virgola.
3. Il primo contiene coppie, ad esempio [(Tom, 1), (Harry, 3)], mentre il secondo contiene un elenco
di stringhe e un unico numero intero, ad esempio ([Tom, Harry], 1).
4. Ciò che viene visualizzato dipende dalla definizione del metodo toString nella classe BankAccount.
5. No, il metodo non ha tipi parametrici, si tratta di un metodo ordinario all’interno di una classe
generica.
6. public class TreeSet <E extends Comparable>
9. La classe supera la compilazione (con un warning), ma si tratta di una tecnica debole e se, in futuro,
non si effettuerà più la cancellazione dei tipi parametrici, questo codice sarà errato: il cast da Object[]
a String[] provocherà il lancio di un’eccezione.
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
( seguito)
Carattere Codice Decimale Carattere Codice Decimale
K '\u 004B' 75 e '\u0065' 101
L ■\u004C' 76 f '\u0066' 102
M '\u 004D' 77 g '\u0067' 103
N '\u 004E' 78 h '\U0068' 104
O '\u 004F' 79 i '\u0069' 105
P '\u0050' 80 j '\u006A' 106
Q '\u005l' 81 k '\u006B' 107
R '\u0052' 82 1 '\u006C' 108
S '\u0053' 83 ni '\U006D' 109
T '\u0054' 84 n '\u006E' 110
U '\u0055' 85 O '\u006F' 111
V '\u 0056' 86 P '\u0070' 112
W '\u0057' 87 q '\u007l' 113
X '\u 0058' 88 r '\u0072' 114
Y '\u0059' 89 S '\u0073' 115
Z '\u 005A' 90 t '\u0074' 116
[ '\u 005B' 91 U '\u0075' 117
\ '\u005C' 92 V '\u 0076' 118
] ■\u 005D' 93 W '\u0077' 119
A '\u 005E' 94 X '\u 0078' 120
'\u 005F' 95 y '\u0079' 121
'\U0060' 96 Z '\u007A' 122
a '\u006l' 97 { '\u 007B’ 123
b '\u0062' 98 I '\u007C' 124
C '\u0063' 99 I '\u 007D' 125
d '\u0064' 100 ~ ’\u 007E’ 126
Tabella 2
Carattere Sequenza Decimale Codice
Alcuni caratteri di escape
di controllo
Tab ' \f 9 '\u0009'
Nuova riga '\n' 10 '\u000A'
Invio '\r' 13 ■\u000D'
Spazio 32 '\u0020'
B
Linguaggio Java:
operatori
Gli operatori sono elencati, nella tabella che segue, in gruppi di precedenza decrescente.
Ad esempio, z = x - y; significa z = (x - y); perché = ha una precedenza più bassa di
( continua)
780 A p p e n d ic e B
( seguito)
( continua)
782 A p p e n d ic e C
( sej;>uito)
Parola riservata Descrizione
float II tipo in virgola mobile con singola precisione a 32 bit
for Un ciclo con inizializzazione, condizione e espressioni di aggiornamento
goto Non usata
if Un enunciato per una diramazione condizionata
implements Indica che una classe realizza un’interfaccia
import Consente l’uso di nomi di classe senza ilnome del pacchetto
instanceof Verifica se il tipo di un oggetto èuguale altipospecificato o a una sua sotto
classe
int II tipo intero a 32 bit
interface Un tipo astratto con soli metodi astratti o predefmiti e costanti
long II tipo intero a 64 bit
native Un metodo realizzato in un linguaggio diverso da Java
new Crea un nuovo oggetto
package Una raccolta di classi correlate
private Una caratteristica che è accessibile soltanto da metodi della stessa classe
protected Una caratteristica che è accessibile soltanto da metodi della stessa classe,
di una sottoclasse o di un’altra classe nello stesso pacchetto
public Una caratteristica che è accessibile da qualsiasi metodo
return Termina un metodo
short II tipo intero a 16 bit
static Una caratteristica definita per una classe, invece che per singoli esemplari
strictfp Usa regole stringenti per i calcoli in virgola mobile
super Invoca il costruttore della superclasse o un suo metodo
switch Un enunciato di selezione
synchronized Un blocco di codice che è accessibile da un solo thread per volta
this II parametro implicito di un metodo oppure l’invocazione di un altro costruttore
della classe
throw Lancia un’eccezione
throws Le eccezioni che possono essere lanciate da un metodo
transient Variabili di esemplare che non dovrebbero essere serializzate
try Un blocco di codice con gestori di eccezione o con un gestore finally
void Contrassegna un metodo che non restituisce alcun valore
volatile Una variabile che potrebbe essere aggiornata da più thread senza sincroniz
zazione
while Un enunciato di ciclo
D
Sistemi di numerazione
JumeàlÈiatL.
La notazione decimale rappresenta i numeri come potenze di 10, ad esempio
Non c’è nessun motivo particolare per la scelta del numero 10, a parte il fatto che molti
sistemi di numerazione sono stati messi a punto da persone che contavano con le dita:
altri sistemi numerici (con base 12, 20 o 60) sono stati usati da varie culture nel corso
della storia dell’umanità. I computer, invece, usano un sistema numerico con base 2
perché è molto più facile costruire componenti elettronici che funzionino con due soli
valori, che possono essere rappresentati da una corrente che scorre oppure no, piuttosto
che rappresentare 10 valori diversi di un segnale elettrico. Un numero scritto in base 2
viene anche detto numero binario. A d esempio
In generale, per convertire un numero binario nel suo equivalente decimale, si valutano
semplicemente le potenze di 2 che corrispondono alle cifre di valore 1 e si sommano. La
Tabella 1 mostra le prime potenze di 2.
784 A p p e n d ic e D
Tabella 1
2" 1
Potenze di due 2
2‘
2~ 4
2^ 8
24
16
2^ 32
2^ 64
2^ 128
2« 256
r 512
210 1024
2’> 2048
2^2 4096
2'^ 9192
214
16384
215
32768
216
65536
100 I 2 = 50 resto 0
50 I2 = 25 resto 0
25 I 2 = 12 resto 1
12 I2 = 6 resto 0
6 I2 = 3 resto 0
3 I2 = 1 resto 1
1 I2 = 0 resto 1
0.35 • 2 = 0.7
0.7 - 2 = 1.4
0.4 • 2 = 0.8
0.8 • 2 = 1.6
0. 6 • 2 = 1.2
0.2 • 2 = 0.4
Per capire che cosa giustifica un risultato così curioso, si può eseguire la lunga
moltiplicazione a mano:
1 1 0 0 1 0 0 * 1 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
1 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
1 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
0
0
1 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
0
0
1 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0
Il risultato ha 33 bit, quindi non trova posto nei 32 bit di un valore di tipo int e il primo
bit viene ignorato: gli altri 32 bit sono la rappresentazione binaria di 705032704 (infatti
il bit ignorato vale 2^^ = 4294967296 e la somma dei due valori è proprio 5000000000,
il risultato corretto).
Elaborando numeri in virgola mobile si può verificare un altro tipo di errore: Terrore
di arrotondamento {roundoff). Consideriamo questo esempio:
Per capire come si possa verificare questo errore, eseguiamo anche in questo caso la
moltiplicazione in colonna:
Quindi, il risultato è 434 seguito da un numero infinito di cifre 1: la sua parte frazionaria è
l’equivalente binario del numero decimale periodico 0 .9 9 9 9 9 9 .che è matematicamente
uguale a 1. Ma il computer può memorizzare soltanto un numero finito di cifre e quando
converte il risultato in un numero decimale ne ignora alcune.
Per rappresentare numeri interi negativi esistono due comuni notazioni, chiamate “modulo
e segno” e “complemento a due”. La notazione con modulo e segno è semplice: si usa
il bit più a sinistra per il segno (0 = positivo, 1 = negativo). Ad esempio, usando numeri
di 8 bit:
Tuttavia, costruire circuiti per sommare numeri diventa un po’ più complicato quando
occorre considerare il segno. La rappresentazione in complemento a due risolve questo
problema. Per comporre il complemento a due di un numero:
Ad esempio, per calcolare -13 come valore di 8 bit, dapprima cambiate il valore di tutti i bit
di OOOOl 101 (che è la rappresentazione di 13), ottenendo 11110010, quindi aggiungete 1:
_i ^ = 1111
1 1 1 1
noi1 11 complemento a due
Con questa notazione non serve nessun circuito specifico per sommare due numeri: seguite
semplicemente le normali regole dell’addizione, con il riporto nella posizione successiva
nel caso in cui la somma delle cifre e del riporto precedente sia 2 o 3. Ad esempio:
1 1111 111
+13 0000 1101
-13 1111 0011
1 0000 0000
Sono importanti, però, soltanto gli ultimi 8 bit, per cui +13 e -13 hanno somma 0,come
dovrebbero.
In particolare, la rappresentazione in complemento a due di -1 è 1111... 1111, cioè
tutti i bit valgono 1.
11bit più a sinistra di un numero in complemento a due vale 0 se il numero è positivo
e 1 se è negativo.
La rappresentazione in complemento a due con un determinato numero di bit può
rappresentare un numero negativo in più rispetto al numero di valori positivi rappresen
tabili; ad esempio, i numeri in complemento a due con 8 bit variano da -128 a +127.
Questo fenomeno è fonte di errori di programmazione. Ad esempio, considerate il
codice seguente:
S istemi di n u m e r a z i o n e 787
short b = . .
if (b < O) { b = (byte) -b; }
f
Questo codice non garantisce che, al termine, b sia non negativo. Se b vale inizialmente
—128, il calcolo del suo opposto fornisce nuovamente il valore —128. Provate: prendete
10000000, cambiate tutti i bit, e sommate 1.
□ un bit di segno
□ un esponente
□ una mantissa
1 numeri in virgola mobile usano la notazione scientifica, nella quale un numero viene
rappresentato come
^^^decimale ~ ~ ^ ^
,
100decimale = 0 110 0 0 0 10 11 10010000000000000000000 IEEE singola precisione
Doppia precisione
Poiché i numeri binari sono di difficile lettura per le persone, spesso i programmatori
usano il sistema di numerazione esadecimale, con base 16. Le cifre vengono indicate con
0,1, . . 9, A, B, C, D, E, F (osservate la Tabella 2).
Quattro cifre binarie corrispondono a una cifra esadecimale: ciò rende semplici le
conversioni fra valori binari e valori esadecimali. Ad esempio
In Java, i numeri esadecimali sono usati come valori per i caratteri Unicode, ad esempio
\ u03Bi (la lettera greca alfa minuscola). I numeri interi esadecimali vengono indicati con
il prefisso Ox, come, ad esempio, oxSBi.
InJava sono disponibili quattro operazioni che agiscono su bit: la negazione unaria (~) e
le operazioni binarie a n d (&), or (|) e or esclusivo (''), detto anche xor.
Tabella 2
jcimale Decim ale Binario
Cifre esadecimali
0 0 0000
1 1 OOOl
2 2 OOlO
3 3 OOll
4 4 OlOO
5 5 OlOl
6 6 Olio
7 7 Olii
8 8 1000
9 9 1001
A 10 1010
B 11 1011
C 12 1100
D 13 noi
E 14 ino
F 15 1111
S is t e m i d i n u m e r a z io n e 789
Le Tabelle 3 e 4 mostrano le tabelle di verità per le operazioni sui bit in Java. Quando
un’operazione sui bit viene applicata a numeri interi, l’operazione viene eseguita sui bit
corrispondenti.
Tabella 1
L'operazione
di negazione unaria
Tabella 2
a b a&b a Ib a^b
Le operazioni binarie and,
or ex o r 0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
Ad esempio, supponete di voler calcolare 46 & 1 3 . Per prima cosa convertite in binario
entrambi i valori: = 1011 (in realtà, essendo un numero intero a 32
bit, 00000000000000000000000000101110); 13dedmale = effettuate
l’operazione sui bit corrispondenti:
0 ...... 0101110
& 0 ..... 0001101
0 ...... 0001100
La risposta è l i 00b.„aHo = 1
A volte si vede l’operatore | usato per combinare due schemi di bit.Ad esempio. Font.
BOLDha il valore 1, Font.ITALIC ha il valore 2. La combinazione Font.BOLD | Font.ITALIC ha
impostati a 1 sia il bit per il grassetto sia quello per il corsivo:
0 .... 0000001
I 0...... 0000010
0 .... 0000011
Non confondete gli operatori per i bit & e | con gli operatori && e 11. Questi ultimi
operano soltanto su valori di tipo boolean, e non sui bit.
Oltre alle operazioni che agiscono su singoli bit, esistono anche tre operazioni di
scorrim ento (shift) che prendono lo schema di bit di un numero e lo spostano a sinistra o
a destra di un certo numero di posizioni. Esistono tre operazioni di scorrimento: scorri
mento a sinistra (<<), scorrimento a destra con estensione del segno (>>) e scorrimento a
destra con estensione di zeri (>>>).
Lo scorrimento a sinistra sposta tutti i bit verso sinistra, inserendo zeri nei bit meno
significativi. Lo spostamento a sinistra di n bit fornisce lo stesso risultato di una molti
plicazione per 2'L Lo scorrimento verso destra con estensione del segno sposta tutti i bit
a destra, propagando il bit di segno: quindi, il risultato è uguale a quello della divisione
intera per 2", sia per valori positivi che per valori negativi. Infine, lo scorrimento a destra
790 A p p e n d ic e D
\ \ \ \ . \ V
W W W
0 0 \
con estensione di zeri sposta tutti i bit a destra, inserendo zeri nei bit più significativi
(osservate la Figura 2).
Notate che il valore a destra dell’operatore di scorrimento viene usato modulo 32
(per valori di tipo int) o modulo 64 (per valori di tipo long), determinando così !’effettivo
numero di posizioni di cui spostare i bit.
Ad esempio, i << 35 è uguale a l << 3. Spostare veramente il numero 1 verso sinistra
di 35 posizioni non avrebbe senso: il risultato sarebbe 0.
L’espressione
1 << n
genera uno schema di bit in cui il solo bit n-esimo vale 1 (contando le posizioni a partire
dalla posizione 0 del bit meno significativo).
Per impostare a 1 il bit n-esimo di un numero, eseguite l’operazione seguente:
X = X I 1 << n
Notate che le parentesi attorno all’operatore &sono necessarie, perché ha una precedenza
minore degli operatori relazionali.
Glossario
Accesso casuale La possibilità di accedere direttamente eseguito aH’interno di un browser web o di un apposito
a qualsiasi valore senza dover leggere i valori che lo visualizzatore di applet.
precedono. Argomento Un parametro effettivo fornito nell’invoca
Accesso sequenziale Accesso a valori in sequenza, senza zione di un metodo oppure uno dei valori (operandi) su
saltarne alcuno. cui agisce un operatore.
Accoppiamento II grado di mutua correlazione tra classi Array Una raccolta di valori del medesimo tipo, disposti
per effetto di dipendenze. in posizioni di memoria contigue, a ciascuno dei quali
Adattatore per eventi Una classe che realizza un’interfac si può accedere mediante un indice intero.
cia per ricevitore di eventi definendo tutti i suoi metodi Array bidimensionale Una disposizione tabulare di ele
in modo che non compiano alcuna azione. menti in cui si accede a ciascun elemento mediante un
ADT (Abstract Data Type, tipo di dato astratto) Una indice di riga e un indice di colonna.
specifica delle operazioni fondamentali che caratterizzano Array paralleli Array aventi la stessa lunghezza, in cui
un tipo di dati, senza fornirne una realizzazione. clementi corrispondenti sono tra loro correlati dal punto
Algoritmo Una specifica del modo di risolvere un pro di vista logico.
blema che sia non ambigua, eseguibile e che termini in Array riempito solo in parte Un array che non è ri
un tempo finito. empito per la sua intera capacità, accompagnato da una
Ambito di visibilità La porzione di programma in cui è variabile che indica il numero di elementi che vi sono
definita e visibile una variabile. realmente memorizzati.
Apertura di un file Predisposizione di un file per opera Assegnazione Memorizzazione di un nuovo valore in
zioni di lettura o di scrittura. una variabile.
API (Application Programming Interface) Una libre Asserzione L’affermazione che una certa condizione sia
ria di codice utilizzata per costruire programmi. vera in un particolare punto di un programma.
Applet Un programma grafico scritto in Java che viene Associatività degli operatori La regola che stabilisce in
792 G l o s s a r io
quale ordine debbano essere eseguiti operatori aventi la (int) Xesprime, in Java, la conversione forzata in numero
medesima precedenza. Ad esempio, in Java l’operatore - è intero del valore contenuto in x.
associativo a sinistra, per cui a - b - c viene interpretato Ciclo Una sequenza di istruzioni che viene eseguita ri
come (a - b) - c, mentre l’operatore = è associativo a de petutamente.
stra, per cui a = b = C viene interpretato come a = (b = c). Ciclo annidato Un ciclo contenuto all’interno di un
Auto-boxing Conversione automatica di un valore di altro ciclo.
tipo primitivo in un esemplare di una classe involucro Ciclo e mezzo Un ciclo la cui decisione di terminazione
{wrapper). non si trova né all’inizio né alla fine.
Bit Cifra binaria; la più piccola unità di informazione, con Classe Un tipo di dato definito dal programmatore.
due possibili valori: Ge l . Un dato composto da n bit ha Classe anonima Una classe priva di nome.
2" possibili valori. Classe astratta Una classe di cui non si possono creare
Blocco Un gruppo di enunciati racchiusi tra parentesi esemplari.
graffe. Classe concreta Una classe di cui si possono creare
Breakpoint Una posizione nella quale si desidera l’arresto esemplari.
del debugger, per ispezionare lo stato del programma in Classe generica Una classe con tipi parametrici.
esecuzione controllata. Classe immutabile Una classe priva di metodi modifi
Buffer Una zona di memoria usata temporaneamente per catori.
memorizzare valori (ad esempio, caratteri digitati dall’u Classe interna Una classe definita all’interno di un’altra
tente) che sono in attesa di essere utilizzati (ad esempio, classe.
leggendo una riga per volta). Classe involucro (wrapper) Una classe, come Integer, che
Bug Un errore di programmazione. contiene un valore di un tipo primitivo.
Byte Una quantità di informazione pari a otto bit.Tutti gli Classe per eventi Una classe che contiene informazioni
attuali produttori di calcolatori usano il byte come unità in merito a un evento dell’interfaccia grafica; ad esempio,
elementare di memorizzazione. la sorgente dell’evento stesso.
Bytecode Istruzioni per la macchina virtuale Java. Clausola catch Parte di un blocco try che viene eseguita
Callback Un meccanismo che consente di specificare un quando un qualsiasi enunciato interno al blocco try lancia
blocco di codice da eseguire successivamente. un’eccezione catturata dalla clausola.
Carattere Una singola lettera, una cifra o un simbolo. Clausola finally Parte di un blocco try che viene eseguita
Carattere di escape Un carattere che non va interpretato indipendentemente dal modo in cui termina il blocco
in modo letterale, ma ha un significato speciale quando try stesso.
viene combinato con il carattere (o i caratteri) seguente. Clausola throws Specifica il tipo di eccezioni a controllo
Il carattere \ è un carattere di escape nelle stringhe Java. obbligatorio che possono essere lanciate da un metodo.
Carattere di nuova riga (newline) Il carattere '\n^ che Coda Una raccolta di elementi che vengono estratti con la
segnala la fine di una riga. strategia “il primo entrato è il primo a uscire”.
Carattere di tabulazione II carattere \ che sposta il Coda prioritaria Una raccolta che consente di eseguire in
successivo carattere lungo la riga in modo che si allinei modo efficiente l’inserimento di elementi e la rimozione
alla successiva posizione prefissata, denominata “posizione dell’elemento di valore minimo.
di tabulazione’’. Codice di hash Un valore calcolato da una funzione di
Caratteri di spaziatura (white space) L’insieme dei ca hash.
ratteri di spazio, tabulazione e nuova riga. Codice macchina Istruzioni che possono essere eseguite
Cartella (directory) Una struttura di un file system che è in direttamente dalla CPU.
grado di contenere file o altre cartelle. Codice sorgente Istruzioni, espresse in un linguaggio di
Caso limite Una situazione da collaudare che coinvolge programmazione, che necessitano di traduzione prima di
valori che si trovano al limite del proprio insieme di poter essere eseguite da un calcolatore.
validità. Ad esempio, il valore zero è un caso limite per Coesione Una classe è coesa se le sue caratteristiche de
collaudare una funzione che elabora valori non negativi. scrivono un’unica astrazione.
Cast Conversione esplicita (“forzata’’) di un valore da un Collaudo “a scatola bianca” Un collaudo progettato,
tipo a un tipo diverso. Ad esempio, se x è una variabile al contrario del collaudo “a scatola nera”, prendendo
che contiene un numero in virgola mobile, la notazione in esame !’implementazione della classe da collaudare,
G l o s s a r io 793
ad esempio selezionando accuratamente i casi limite e CRC (scheda) Una scheda che rappresenta una classe e
garantendo la copertura di tutte le diramazioni del codice. ne elenca responsabilità e collaborazioni.
Collaudo “a scatola nera” Un collaudo progettato senza Debugger Un programma che consente all’utente l’e
conoscere !’implementazione del metodo da collaudare. secuzione passo dopo passo di un altro programma,
Collaudo di unità 11 collaudo di un metodo a sé stante, con la possibilità di interromperne l’esecuzione e di
isolato dal resto del programma. ispezionarne le variabili, per agevolare l’identificazione
Collaudo regressivo Raccolta di tutti i casi di prova per degli errori.
utilizzarli nel collaudo di tutte le successive revisioni di Diagramma sintattico Una rappresentazione grafica di
un programma. regole sintattiche.
Collisione Si ha quando una funzione di hash calcola il Directory Vedi Cartella
medesimo codice per due oggetti diversi. Disco rigido Un dispositivo che memorizza informazioni
Commento Una spiegazione che aiuta un lettore umano a su dischi rotanti ricoperti di materiale magnetico.
comprendere una porzione di programma; viene ignorata Divisione intera Fornisce il quoziente della divisione tra
dal compilatore. due numeri interi, ignorando l’eventuale resto. In Java,
Commento per la documentazione Un commento quando entrambi gli operandi sono interi, il simbolo /
all’interno di un file sorgente che può essere estratto indica la divisione intera: ad esempio, 11/4 vale 2, non 2.75.
automaticamente da un programma come javadoc per Documentazione API Informazioni relative alle classi
generare la documentazione del programma. della libreria di Java.
Compilatore Un programma che traduce codice scritto Eccezione Una classe che segnala una condizione che im
in un linguaggio di alto livello (come Java) in istruzioni pedisce la normale prosecuzione del programma: quando
macchina (come le istruzioni bytecode per la macchina si verifica tale condizione, viene lanciato un esemplare
virtuale Java). di una classe eccezione.
Componente dell’interfaccia utente Un blocco costi Eccezione a controllo non obbligatorio U n ’eccezione
tutivo dell’interfaccia grafica di interazione con l’utente, la cui cattura non viene resa obbligatoria dal compilatore.
come un pulsante o un campo di testo. 1 componenti Eccezione a controllo obbligatorio U n’eccezione la
dell’interfaccia utente vengono utilizzati per presentare cui cattura viene resa obbligatoria dal compilatore: deve
informazioni all’utente e per consentire a quest’ultimo essere dichiarata o gestita.
di fornire informazioni al programma. Editor Un programma utilizzato per scrivere e modificare
Concatenazione L’accodamento di una stringa al termine file di testo.
di un’altra stringa, per formare una stringa più lunga. Effetto collaterale Un effetto visibile di un metodo, di
Conflitto tra nomi L’utilizzo accidentale di uno stesso verso dal valore restituito.
nome per indicare due diverse caratteristiche di un pro Enumerazione Un tipo di dato che può assumere un
gramma in un modo che il compilatore non è in grado numero finito di valori, ciascuno dei quali è dotato di
di dirimere. un proprio nome simbolico.
Contesto grafico Una classe mediante la quale un pro Enunciato U n’unità sintattica all’interno di un programma.
grammatore può disegnare forme grafiche all’interno di In Java, un enunciato può essere un enunciato semplice,
una finestra o in un file. un enunciato composto o un blocco di enunciati.
Copertura del codice La percentuale delle istruzioni di un Enunciato break Enunciato che pone termine a un ciclo
programma che vengono eseguite durante un collaudo. O a un enunciato switch.
Corpo L’insieme degli enunciati di un metodo o di un Enunciato try Un enunciato il cui corpo contiene enun
blocco. ciati che vengono eseguiti fino al termine del corpo
Costante Un valore che non può essere modificato dal stesso oppure fino al lancio di un’eccezione; contiene
programma. In Java, le costanti vengono definite mediante anche clausole che vengono invocate quando si verifica
la parola riservata final. una specifica eccezione.
Costruttore Una sequenza di enunciati che inizializza un Enunciato try con risorse Un versione dell’enunciato
oggetto appena creato. try la cui intestazione inizializza una variabile con un
Costruzione Impostazione dello stato iniziale di un oggetto esemplare di una classe che implementa l’interfaccia Au-
appena creato. toCloseable, il cui metodo dose viene invocato quando
CPU (Central Processing Unit) La parte di calcolatore l’enunciato try termina, normalmente o per il lancio di
che esegue le istruzioni in linguaggio macchina. un’eccezione.
794 G l o s s a r io
Ereditarietà La relazione che esiste fra una superclasse, più File di testo Un file in cui i valori dei dati vengono me
generica, e una sottoclasse, più specifica. morizzati nella loro rappresentazione testuale.
Errore di arrotondamento Un errore dovuto al fatto File sorgente Un file contenente istruzioni espresse in un
che il calcolatore può memorizzare soltanto un numero linguaggio di programmazione, come Java.
finito di cifre di un numero in virgola mobile. Finestra di shell Una finestra che consente di interagire
Errore di compilazione Un errore che viene identificato con il sistema operativo mediante comandi di tipo te
durante la compilazione di un programma. stuale.
Errore di limiti Tentativo di accesso a un elemento di un Flag Vedi Tipo booleano.
array che si trova al di fuori dell’intervallo di indici leciti. Flusso (stream) U n’astrazione di una sequenza di byte da
Errore di sintassi U n’istruzione che non segue le regole cui si possono leggere o in cui si possono scrivere dati.
sintattiche del linguaggio di programmazione e viene, Font Un insieme di forme di caratteri aventi una specifica
quindi, rifiutata dal compilatore (un tipo di errore di dimensione e un particolare stile.
compilazione). Frame Una finestra con un bordo e una barra del titolo.
Errore in esecuzione o logico Un errore che si verifica Funzione Una funzione matematica restituisce un risul
durante l’esecuzione di un programma sintatticamente tato per ogni possibile assegnazione di valori ai suoi
corretto, portandolo ad agire in modo diverso da quanto parametri. In Java, le funzioni possono essere imple
previsto. mentate mediante espressioni lambda o esemplari di
Errore per scarto di uno Un frequente errore di pro interfacce funzionali.
grammazione che si verifica quando un valore è di un’u Funzione di hash Una funzione che calcola un numero
nità più grande o più piccolo di quanto dovrebbe essere. intero a partire da un oggetto, in modo che sia molto
Esemplare di una classe Un oggetto il cui tipo è una probabile che per oggetti distinti vengano calcolati valori
classe. distinti.
Espressione Un costrutto sintattico composto di costanti, Garbage collection Recupero automatico della memo
variabili, invocazioni di metodi e operatori che li com ria occupata da oggetti ai quali nessuna variabile fa più
binano. riferimento.
Espressione canonica Una stringa che definisce un in Gestore di eccezioni Una sequenza di enunciati a cui
sieme di stringhe selezionate in base al loro contenuto. viene demandato il controllo d’esecuzione quando è stata
Ciascuna parte di un’espressione canonica può essere: lanciata e catturata un’eccezione di un particolare tipo.
uno specifico carattere, che in tal modo diviene essen Gestore di eventi Un metodo che viene eseguito quando
ziale; un carattere appartenente a un insieme di caratteri accade un particolare evento.
ammessi,come [abc],che può anche essere un intervallo, grep Un programma che effettua ricerche mediante espres
come [a-z]; qualsiasi carattere che non appartenga a un sioni canoniche.
insieme di caratteri proibiti, come ["'0-9]; la ripetizione GUI (Graphical User Interface) U n’interfaccia mediante
di una o più corrispondenze, come [0-9]+, oppure zero la quale l’utente fornisce dati a un programma usando
0 più, come [ACCI]*; un’opzione scelta in un insieme di componenti grafici come pulsanti, menu e campi di testo.
alternative, come and|et|und; oppure, varie altre condi Hardware La parte fisica di un calcolatore o di un altro
zioni e possibilità. Ad esempio, all’espressione canonica dispositivo.
"[A-Za-z]*[0-9]+" corrispondono "Cloud9" oppure "007", IDE (Integrated Development Environment) Un
ma non "Jack". ambiente di programmazione che contiene un editor,
Espressione lambda U n’espressione che definisce con un compilatore e un debugger.
una notazione compatta i parametri e il valore restituito Implementazione di un’interfaccia Realizzazione
da un metodo. di una classe che definisce tutti i metodi specificati da
Estensione L’ultima parte del nome di un file, che ne spe un’interfaccia.
cifica la tipologia. Ad esempio, l’estensione . java identifica Importazione di una classe o di un pacchetto Segnala
1 file che contengono codice sorgente Java. l’intenzione di fare riferimento a una classe o a tutte le
Evento dell’interfaccia utente La segnalazione, che viene classi contenute in un pacchetto usando semplicemente
notificata al programma, di un’azione compiuta dall’u il suo nome invece del nome “completo”.
tente, come la pressione di un tasto, lo spostamento del Incapsulamento L’occultamento dei dettagli realizzativi.
mouse O la selezione di una voce di menu. Inizializzazione Assegnazione di un valore a una variabile
File Una sequenza di byte memorizzata su un disco. nel momento in cui questa viene creata.
G l o s s a r io 795
Insieme Una raccolta non ordinata di elementi che con Macchina di Turing Un modello di elaborazione estrema-
sente di effettuare in modo efficiente l’inserimento, mente semplice che viene usato nell’informatica teorica
l’eliminazione e la ricerca di elementi. per esplorare problemi di computabilità.
Interfaccia Un tipo di dato privo di variabili di esem Macchina virtuale Un programma che simula il fun
plare, contenente soltanto costanti e metodi astratti o zionamento di una CPU e che può essere realizzato in
predefmiti. modo efficiente per un’ampia varietà di macchine reali. Il
Interfaccia funzionale U n ’interfaccia che ha un uni bytecode relativo a un programma può essere eseguito da
co metodo astratto e ha lo scopo di definire un’unica qualsiasi macchina virtuale Java, indipendentemente dalla
funzione. CPU utilizzata per eseguire la macchina virtuale stessa.
Interfaccia pubblica Le caratteristiche di una classe Mappa Una struttura di memorizzazione che associa
(metodi, variabili e tipi interni) che sono accessibili agli oggetti che fungono da chiave a oggetti che hanno il
utilizzatori. ruolo di valori.
Istanza Vedi Esemplare Memoria secondaria La memoria persistente, che man
Iteratore Un oggetto che è in grado di ispezionare tutti tiene il proprio contenuto anche in assenza di energia
gli elementi presenti in un contenitore, ad esempio una elettrica (ad esempio, un disco rigido).
lista concatenata. Mergesort Vedi Ordinamento per fusione
javadoc II generatore di documentazione presente nell’am Metodo Una sequenza di enunciati che ha un nome, può
biente di sviluppo Java SDK: estrae dai file sorgenti Java avere variabili parametro e può restituire un valore. Un
i commenti di documentazione e genera un insieme di metodo può essere invocato un numero qualsiasi di volte,
file HTML collegati come ipertesto. con diversi valori attribuiti ai suoi parametri.
Lancio di un’eccezione Indica una condizione anomala e Metodo astratto Un metodo dotato di nome, tipi dei
provoca la terminazione del flusso normale di esecuzione parametri e tipo del valore restituito, ma privo di im
del programma, trasferendo il controllo a una clausola plementazione.
catch appropriata. Metodo d’accesso Un metodo che accede a un oggetto
Legge di De Morgan Una regola riguardante le operazioni senza modificarlo.
logiche: descrive come negare espressioni composte di Metodo predefinito (o di default) Un metodo non sta
operatori and e or. tico di cui si descrive !’implementazione in un’interfaccia.
Letterale La notazione che riguarda un valore fisso all’in- Metodo di esemplare Un metodo avente un parametro
terno di un programma, .come -2, 3.14, 6.02214115E23, implicito, cioè un metodo che viene invocato mediante
"Harry" o 'H'. un esemplare di una classe.
Libreria U n insieme di classi pre-compilate che possono Metodo main II primo metodo invocato quando viene
essere u tilizzate in un programma. eseguita un’applicazione Java.
Limiti asimmetrici Limiti di ciclo inclusivi del valore Metodo modificatore Un metodo che può modificare
iniziale ma non del valore finale dell’indice. lo stato di un oggetto.
Limiti simmetrici Limiti di ciclo inclusivi del valore Metodo ricorsivo Un metodo che invoca se stesso con
iniziale e finale dell’indice. dati più semplici. Deve gestire i casi più semplici senza
Linguaggio di alto livello Un linguaggio di programma invocare se stesso.
zione che privilegia la rapida progettazione di prototipi Metodo statico o di classe Un metodo privo di para
piuttosto della velocità di esecuzione o della facilità di metro implicito.
manutenzione del codice. Mock Vedi Oggetto semplificato.
Lista concatenata Una struttura di memorizzazione che Modulo L’operatore %che calcola il resto di una divisione
può contenere un numero arbitrario di oggetti, ciascuno intera.
dei quali viene conservato in un nodo che contiene un Notazione O-grande La notazione j^(n) = 0(f(n)), che
puntatore al nodo successivo. indica che la funzione^ cresce rispetto a n con un com
Lista doppiamente concatenata Una lista concatenata portamento limitato superiormente dalla crescita della
in cui ciascun nodo contiene sia un riferimento al nodo funzione/A d esempio, 10 + 100 « - 1000 = 0(n~).
precedente sia un riferimento al nodo successivo. Notazione polacca inversa (RPN) Uno stile per la
Locazione di memoria Un valore che specifica la scrittura di espressioni in cui gli operatori vengono
posizione di un dato all’interno della memoria di un indicati dopo i propri operandi. Ad esempio, 2 3 4 * +
calcolatore. equivale a 2 + 3 * 4.
796 G l o s s a r io
Polimorfismo Selezione, in base all’efFettivo tipo del Ricevitore di eventi Un oggetto che, quando accade un
parametro implicito, di uno tra vari metodi aventi lo evento, riceve una notifica dalla sorgente dell’evento
stesso nome. stesso.
Precedenza degli operatori La regola che stabilisce quale Ricorsione Una strategia che calcola un risultato mediante
operatore debba essere valutato per primo. Ad esempio, la scomposizione dei dati da elaborare in valori più sem
in Java l’operatore &&ha la precedenza sull’operatore 11, plici, applicandovi poi la medesima strategia.
per cui l’espressione a 11 b & & c viene valutata come Ricorsione mutua Metodi cooperanti che si invocano
a II (b &&c). reciprocamente.
Principio di sostituzione II principio secondo il quale Riferimento a oggetto Un valore che rappresenta la po
un esemplare di una sottoclasse può essere usato al posto sizione in memoria di un oggetto. In Java, una variabile il
di un esemplare di una sua superclasse. cui tipo sia una classe contiene in realtà un riferimento
Progettazione orientata agli oggetti Progettazione di a un oggetto, esemplare di tale classe.
un programma che avviene mediante la definizione di Riferimento nuli Un riferimento che non si riferisce ad
oggetti, delle loro proprietà e delle loro relazioni. alcun oggetto.
Programma per calcolatore Una sequenza di istruzioni Riga dei comandi La riga in cui l’utente digita i comandi
che può essere eseguita da un calcolatore. necessari per eseguire un programma nel sistema opera
Programma per console Un programma Java privo di tivo DOS, Windows o Unix; è composta dal nome del
interfaccia grafica. Un programma per console legge i programma seguito da suoi eventuali argomenti.
dati in ingresso dalla tastiera e visualizza i dati prodotti Script per shell Un file contenente comandi per l’esecu
in uscita sulla finestra di terminale (console) in cui è stato zione di programmi e la manipolazione di file. Digitando
eseguito. sulla riga dei comandi il nome del file contenente lo
Programmazione La progettazione e realizzazione di script per shell si provoca l’esecuzione dei comandi in
programmi per calcolatore. esso contenuti.
Prompt Un messaggio che invita l’utente a fornire dati Sentinella Un valore di ingresso che non viene usato
in ingresso. come reale valore da elaborare, ma per segnalare la fine
Pseudocodice Una descrizione ad alto livello delle azioni dei dati in ingresso.
intraprese da un programma o da un algoritmo, usando Sequenza di escape Una sequenza di caratteri, come \n
una sintassi informale, mista tra un linguaggio naturale e O \ ”, che inizia con un carattere di escape.
un linguaggio di programmazione. Shadowing Nascondere una variabile definendone un’altra
Quicksort Vedi Ordinamento quicksort. con lo stesso nome.
Raccolta (o collezione) Una struttura di memorizzazione Sintassi Regole che definiscono la composizione delle
dei dati che consente di inserire, rimuovere o ritrovare istruzioni in un particolare linguaggio di programma
elementi. zione.
Redirezione Collegamento dell’ingresso o dell’uscita di Sistema operativo II software che esegue i programmi
un programma a un file, anziché alla tastiera o, rispetti applicativi e fornisce loro servizi (come \\ file system).
vamente, allo schermo. Software Le istruzioni e i dati che regolano il funziona
Rete (di calcolatori) Un sistema di elaborazione costituito mento di un calcolatore o di un altro dispositivo.
da calcolatori e altri dispositivi interconnessi. Sorgente di eventi Un oggetto che è in grado di inviare
Ricerca binaria Un veloce algoritmo che cerca un valore ad altri oggetti notifiche relative a specifici eventi.
all’interno di un array ordinato, dimezzando a ogni passo Sottoclasse Una classe che eredita variabili e metodi da
la porzione di array in cui viene effettuata la ricerca. una superclasse, con la possibilità di aggiungere variabili
Ricerca dinamica del metodo Selezione del metodo da di esemplare e di aggiungere o sovrascrivere metodi.
invocare durante l’esecuzione. In Java, la ricerca dinamica Sovraccarico (overloading) Attribuire più di un significato
del metodo effettua la selezione sulla base della classe al nome di un metodo.
dell’oggetto che funge da parametro implicito. Sovrascrittura (overriding) Ridefinizione di un metodo
Ricerca lineare o sequenziale Ricerca di un oggetto all’interno di una sottoclasse.
all’interno di un contenitore (come un array o una lista) Specificatore di accesso Una parola riservata che indica
che avviene mediante l’ispezione di ciascun suo elemento, le modalità di accesso a una caratteristica; ad esempio,
uno dopo l’altro. public O private.
798 G l o s s a r io
Stato II valore attuale di un oggetto, determinato dall’a Unicode Una codifica standard che assegna valori di due
zione cumulativa di tutti i metodi che sono stati con byte ai caratteri usati nelle lingue scritte di tutto il mondo.
esso invocati. Java memorizza tutti i caratteri usando i rispettivi codici
Stringa Una sequenza di caratteri. Unicode.
Superclasse Una classe da cui una classe più specifica URL (Uniform Resource Locator) Nel World Wide
(denominata sottoclasse) può ereditare. Web, un riferimento a una risorsa informativa, come una
Swing Un insieme di strumenti Java per la realizzazione di pagina HTML o un’immagine.
interfacce grafiche per l’interazione con l’utente. Valore restituito II valore restituito da un metodo me
Tabella hash Una struttura in cui gli elementi vengono diante un enunciato return.
messi in corrispondenza con posizioni all’interno di un Valutazione in cortocircuito Valutazione di una sola
array in base ai valori loro assegnati da una funzione di porzione di un’espressione, nel caso in cui la parte rima
hash. nente non possa modificare il risultato.
Tipo Un insieme di valori dotato di nome e delle opera Variabile In un programma, un simbolo che identifica una
zioni che con essi si possono svolgere. locazione di memoria che può contenere diversi valori.
Tipo booleano Un tipo di dato che può assumere due Variabile di esemplare Una variabile, definita in una
soli valori: true e false. classe, di cui esiste una copia, con un proprio valore, per
Tipo parametrico Un parametro presente nella dichia ciascun esemplare della classe.
razione di una classe generica o di un metodo generico; Variabile locale Una variabile che ha un blocco di enun
viene sostituito da un tipo effettivo. ciati come ambito di visibilità.
Tipo primitivo In Java, un tipo numerico o il tipo boolean. Variabile non inizializzata Una variabile che non ha
Token Una sequenza di caratteri consecutivi all’interno di ricevuto alcun valore iniziale. In Java, !’utilizzo di una
una sorgente di dati che compongono, nel loro insieme, variabile locale non inizializzata costituisce un errore
un’informazione significativa per l’analisi dei dati in di sintassi.
ingresso. Ad esempio, un token può essere una sequenza Variabile parametro In un metodo, una variabile che
di caratteri diversi dal carattere di spaziatura. viene inizializzata con un valore fornito nel momento
Traccia della pila di esecuzione (stack trace) La visua in cui il metodo viene invocato.
lizzazione della pila di esecuzione, che elenca tutte le Variabile statica Una variabile di cui esiste una sola
invocazioni di metodi in attesa di riprendere la propria copia per l’intera classe, a cui possono accedere, even
esecuzione. tualmente modificandone il valore, tutti i metodi della
UML (Unified Modeling Language) Una notazione che classe stessa.
consente di specificare, visualizzare, costruire e documen void Una parola riservata usata per segnalare che un metodo
tare tutti gli aspetti di un sistema software. non restituisce alcun valore.
508 C apitolo 9
a. X = y;
b. y = x;
c. y =new Sandwich();
d. X= new Sub();
R9.8. Disegnate un diagramma di ereditarietà che mostri le relazioni ereditarie fra le classi
seguenti:
Person (persona)
Employee (dipendente)
Student (studente)
Instructor (docente)
Classroom (aula)
Object
R9.9. Disegnate un diagramma di ereditarietà che mostri le relazioni ereditarie fra le classi
seguenti, utilizzate in un programma orientato agli oggetti per la simulazione di traffico:
• Vehiele (veicolo)
• Car (automobile)
510 C apitolo 9
Truck (autocarro)
Sedan (automobile berlina)
Coupe (automobile sportiva)
PickupTruck (piccolo autocarro)
SportUtilityVehicle (automobile SUV)
Minivan (automobilefamiliare)
Bicycle (bicicletta)
Motorcycle (motociclo)
R9.11. In che modo un cast come (BankAccount) x differisce da un cast tra valori numerici
come (in t) x?
R9.12. Quale di queste condizioni restituisce true? Controllate la documentazione di Java
per identificare le relazioni di ereditarietà e ricordate che System.out è un oggetto di tipo
PrintStream.
Esercizi di programmazione
E9.1. Progettate la classe BasicAccount come sottoclasse di BankAccount in modo che il suo
metodo withdraw non consenta di prelevare una somma di denaro superiore a quella presente
nel conto.
E9.2. Progettate la classe BasicAccount come sottoclasse di BankAccount in modo che il suo
metodo withdraw addebiti una penale di $30 per ogni prelievo che produca uno scoperto.
E9.3. Riprogettate la classe CheckingAccount vista nella sezione Consigli pratici 9.1 in modo
che, durante un mese, il primo scoperto provochi l’addebito di una penale di $20, mentre per
ogni altro scoperto che avvenga nello stesso mese la penale sia $30.
Ereditarietà 511
E9.4. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe NumericQuestion
per gestire domande con risposta numerica. Se la differenza tra la risposta data e il valore
previsto non è maggiore di 0.01, accettate la risposta come corretta.
E9.5. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe FillInQuestion.
Un esemplare di questa classe viene costruito fornendo una stringa che contiene la risposta
corretta preceduta e seguita da un carattere di sottolineatura, come "The inventor of Dava was
_Dames Gosling_". La domanda va visualizzata così:
E9.6. Modificate il metodo checkAnswer della classe Question in modo che ignori la differenza
tra lettere maiuscole e minuscole e la presenza di spazi vuoti in numero maggiore del
previsto. Ad esempio, la risposta "DAMES gosling" deve essere ritenuta equivalente a "Dames
Gosling".
E 9.7. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe
AnyCorrectChoiceQuestion, che consenta domande con più risposte corrette, scelte tra quelle
proposte. L’esaminato deve fornire una qualsiasi delle risposte corrette. La stringa che
memorizza le risposte corrette deve contenerle tutte, separate da spazi. Aggiungete al testo
della domanda istruzioni opportune.
E9.8. Aggiungete alla gerarchia di domande vista nel Paragrafo 9.1 la classe MultiChoiceQuestion,
che consenta domande con più risposte corrette, scelte tra quelle proposte. L’esaminato deve
fornire tutte (e sole) le risposte corrette, separate da spazi. Aggiungete al testo della domanda
istruzioni opportune.
E9.9. Aggiungete il metodo addText alla classe Question e progettate una versione diversa di
ChoiceQuestion che invochi addText invece di memorizzare al proprio interno un vettore con
le risposte disponibili.
E9.10. Aggiungete il metodo toString alle classi Question e ChoiceQuestion.
E9.11. Realizzate una classe Person e due sue sottoclassi. Student e Instructor. Una persona ha
un nome e un anno di nascita, uno studente ha una disciplina di specializzazione e un docente
ha un salario. Per ogni classe scrivete la dichiarazione,! costruttori e il metodo toString. Fornite
un programma di prova per collaudare classi e metodi.
E9.12. Progettate una classe Employee: ogni dipendente ha un nome e una retribuzione.
Progettate, quindi, una classe Manager che erediti da Employee: aggiungete una variabile di
esemplare di tipo String, chiamata department, e scrivete un metodo toString che restituisca
una stringa con il nome, il reparto e la retribuzione. Progettate, infine, una classe Executive
che erediti da Manager. Fornite un metodo toString appropriato in tutte le classi e scrivete un
programma che collaudi classi e metodi.
E9.13. La classe java.awt. Rectangle della libreria standard di Java non mette a disposizione un
metodo per calcolare l’area o il perimetro di un rettangolo. Progettate la classe BetterRectangle,
sottoclasse di Rectangle, che abbia i metodi getPerimeter e getArea, senza aj^iun^ere variabili di
esemplare. Nel costruttore invocate i metodi setLocation e setSize della classe Rectangle. Scrivete
un programma che collaudi i metodi che avete progettato.
E9.14. Risolvete nuovamente l’esercizio precedente ma nel costruttore della classe
BetterRectangle invocate il costruttore della superclasse.
512 C apitolo 9
E9.15. Un “punto etichettato” {labeled point) ha una coordinata x, una coordinata y e una
stringa che funge da etichetta. Definite la classe LabeledPoint con il costruttore LabeledPoint(in t
X , int y, String lab el) e il metodo toString che visualizzi x, y e !’etichetta.
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
622 C a p it o l o 11
Esercizi di riepilogo
R l 1.1. Cosa succede se cercate di aprire in modalità di lettura un file inesistente? Che cosa succede
se cercate di aprire in modalità di scrittura un file inesistente?
R l 1.2. Cosa succede se cercate di aprire un file per scrivere, ma il file o il dispositivo sono protetti
contro la scrittura (cioè sono risorse talvolta definite “a sola lettura”, read-only)? Fate un esperimento
con un breve programma di prova.
R l 1.3. Cosa succede se scrivete dati in un PrintWriter senza chiuderlo? Progettate un programma
di prova e dimostrate come si possano perdere dati.
R l 1.4. Come si apre un file il cui nome contiene una barra rovesciata, come c:\temp\output.dat?
R l 1.5. Se il programma Woozle viene eseguito con il comando
java Woozle -Dname=piglet -INeeyore -v heff.txt a.txt lump.txt
R l 1.9. Quando un programma esegue un enunciato throw, qual è il successivo enunciato che
viene eseguito?
Rl 1.10. Cosa succede se non esiste la clausola catch corrispondente a un’eccezione che viene
lanciata?
★★ R l 1.11. Cosa può fare un programma con l’oggetto eccezione ricevuto da una clausola catch?
★★ Rl 1.12. 11 tipo dell’oggetto eccezione è sempre uguale al tipo dichiarato nella clausola catch che
lo cattura? In caso di risposta negativa, perché?
★
R II.13. A cosa serve l’enunciato try con indicazione di risorse? Fornite un esempio di utilizzo.
★★ Rl 1.14. Cosa succede quando in un enunciato try con indicazione di risorse viene lanciata
un’eccezione, quindi viene invocato il metodo dose e questo, a sua volta, lancia un’eccezione
diversa dalla prima? Quale eccezione viene catturata da una clausola catch circostante? Scrivete
un programma d’esempio e provate.
Rl 1.15. Quali eccezioni possono essere lanciate dai metodi next e nextint della classe Scanner?
Sono eccezioni a controllo obbligatorio oppure no?
Rl 1.16. Supponete che il programma visto nel Paragrafo 11.5 legga un file contenente questi
valori:
1
2
3
4
In g r e s s o / u s c it a E g e s t io n e d e l l e e c c e z io n i 623
Quale sarà il risultato? Come si può migliorare il programma per fare in modo che fornisca una
più accurata segnalazione degli errori?'
R l 1.17. Il metodo readFile visto nel Paragrafo 11.5 può lanciare un’eccezione di tipo NullPoin-
terException? Se sì, in quale situazione?
★★★ R l 1.18. Il codice seguente cerca di chiudere il flusso in cui scrive senza usare un enunciato try
con indicazione di risorse:
PrintWriter out = new PrintWr iter (filename);
try
{
. . . // scrittura dei dati attraverso out
out.closeO :
}
catch (lOException exception)
{
out.closeO :
}
Identificate Io svantaggio di questo approccio. Su^erimentcr. cosa succede se il costruttore di
PrintWriter o il metodo d o s e lancia un’eccezione?
giercizi dijisgjMDUòsofi.
El 1.1. Scrivete un programma porti a termine i seguenti compiti:
Apri un file di nome hello.txt.
Memorizza nel file il messaggio "Hello, World!".
Chiudi il file.
Apri di nuovo lo stesso file.
Leggi il messaggio memorizzandolo in una stringa e visualizzalo.
El 1.2. Scrivete un programma che legga un file, elimini le righe vuote e scriva le righe non vuote
nel file originario, sovrascrivendolo.
E11.3. Scrivete un programma che legga un file, elimini le righe vuote che si trovano all’inizio e
alla fine e scriva le righe rimanenti nel file originario, sovrascrivendolo.
El 1.4. Scrivete un programma che legga un file, una riga per volta, scrivendola nel file di uscita
preceduta dal numero di riga. Se il file letto è:
Mary had a little lamb
Whose fleece was white as snow.
And everywhere that Mary went,
The lamb was sure to go!
I numeri di riga devono essere racchiusi tra i delimitatori /* e */, in modo che il programma possa
essere utilizzato per numerare le righe di un file sorgente Java.
Il programma deve chiedere all’utente i nomi dei file di ingresso e di uscita.
El 1.5. Risolvete di nuovo l’esercizio precedente consentendo all’utente di specificare il nome
dei file sulla riga dei comandi. Se l’utente non lo fa, il programma deve chiedere il nome del file
durante l’esecuzione.
El 1.6. Scrivete un programma che legga un file contenente due colonne di numeri in virgola
mobile, per poi visualizzare il valore medio di ciascuna colonna. Chiedete all’utente di fornire il
nome del file.
El 1.7. Scrivete un programma che chieda all’utente il nome di un file e, poi, ne visualizzi il
numero di caratteri, di parole e di righe.
El 1.8. Scrivete un programma Find che effettui una ricerca in tutti i file specificati sulla riga dei
comandi e visualizzi tutte le righe contenenti una parola specifica, fornita come primo argomento
sulla riga dei comandi. Per esempio, se eseguite
java Find ring report.txt address.txt Homework.java
il p r o g r a m m a potrebbe stampare
El 1.9. Scrivete un programma che controlli l’ortografia di tutte le parole presenti in un file. Deve
leggere ciascuna parola del file e controllare se è presente in un elenco di parole, disponibile nei
sistemi Macintosh e Linux nel file /usr/share/dict/words (e facilmente reperibile anche in Internet).
II programma deve visualizzare tutte le parole del file che non sono presenti nell’elenco.
El 1.10. Scrivete un programma che sostituisca in un file ciascuna riga con la sua inversa. Per
esempio, se eseguite
java Reverse HelloPrinter.java
al termine della sua esecuzione il file output.txt dovrà avere questo contenuto:
The lamb was sure to go.
And everywhere that Mary went
Whose fleece was white as snow
Mary had a little lamb
E l 1 . 12. Recuperate i dati relativi ai nomi usati nei decenni scorsi dal sito della Social Security
Administration, inserendoli in file di nome babynames80s.txt e così via. Modificate il programma
BabyNames. java visto nella sezione Esempi completi 11.1 in modo che chieda all’utente il nome di
un file. I numeri all’interno dei file sono separati da virgole, quindi dovete modificare il programma
in modo che gestisca questo formato. Siete in grado di individuare una tendenza, osservando le
frequenze?
E l 1.1 3 .Scrivete un programma che chieda all’utente di inserire un insieme di valori in virgola
mobile. Quando viene inserito un valore che non è un numero, date all’utente una seconda possi
bilità di introdurre un valore corretto e, dopo due tentativi, fate terminare il programma. Sommate
tutti i valori che sono stati specificati in modo corretto e, quando l’utente ha terminato di inserire
dati, visualizzate il totale. Usate la gestione delle eccezioni per identificare i valori di ingresso non
validi.
Modificate la classe BankAccount in modo che lanci IllegalArgumentException quando viene
E l 1 .1 4 .
costruito un conto con saldo negativo, quando viene versata una quantità di denaro negativa o
quando viene prelevata una somma che non sia compresa tra 0 e il saldo del conto. Scrivete un
programma di collaudo che provochi il lancio di tutte e tre le eccezioni, catturandole.
E l 1 .15. Ripetete l’esercizio precedente, ma lanciate eccezioni di tre tipi definiti da voi.
Modificate la classe DataSetReader in modo che non invochi hasNextInt né hasNextDouble.
E l 1.1 6 .
Semplicemente, lasciate che nextint e nextDouble lancino un’eccezione di tipo NoSuchElementException,
catturandola nel metodo main.
Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
674 C a p it o l o 12
Un esempio di ricorsione complessa che non si può risolvere con un semplice ciclo
• Le p e r m u ta zio n i di una stringa si p o s so n o o tten er e in m o d o p iù naturale tram ite la ric o rsio n e
p iu tto sto c h e u san d o u n ciclo .
Usare il backtracking per risolvere problemi che richiedono l'esplorazione di più percorsi
• La tecn ica di b ack track in g esam ina so lu z io n i parziali, ab b an d o n a n d o q u elle ch e n o n p orteran
n o a nulla e torn a n d o sui propri passi p er p rendere in esam e altri candidati alla so lu z io n e finale.
R 1 2 . 6 . S criv ete un a d e fin iz io n e ricorsiva di x ”, c o n « > 0 , c h e sia sim ile alla d e fin iz io n e ricorsiva
d ei n u m er i di F ib o n a cci. S u g g e r im e n to : c o m e si calcola a partire da C o m e si fa term in are
la rico rsio n e?
R 1 2 . 8 . S criv ete un a d e fin iz io n e ricorsiva di n\ = 1 X 2 X ... X « c h e sia sim ile alla d e fin iz io n e
ricorsiva d ei n u m er i di F ib o n a cci.
R l 2 . 9 . S c o p r ite q u an te v o lte la v e rsio n e ricorsiva di fib in voca se stessa. U sa te una variab ile statica,
fibCount, e in crem en ta tela d o p o o g n i in v o c a z io n e di fib. Q u a l è la rela zio n e tra fib(n) e fibCount?
R 1 2 . 1 2 . L’E sercizio P 1 2 .5 m ostra una strategia iterativa p er gen erare tu tte le p e r m u ta zio n i della
seq u en za (0, 1 1 ) . S p ieg a te per q u ale m o tiv o l ’a lg o r itm o p r o d u ce il risultato corretto.
Issrcizi di proatampiazione,
* E 1 2 . 1 . In una classe Rectangle dotata d e lle variabili di esem p lare width e height (risp ettivam en te,
largh ezza e altezza d el retta n g o lo ), d e fin ite il m e to d o rico rsiv o get Area: c o stru ite un r etta n g o lo la
cu i largh ezza sia in ferio re di u n ’u n ità risp etto all’o r ig in a le e in v o c a ten e il m e to d o get Area.
E 1 2 . 2 . In una classe Square dotata della variab ile di esem p lare width (d im e n sio n e del lato del
qu ad rato), d e fin ite il m e to d o rico rsiv o get Area: co stru ite un qu adrato il cu i lato sia in ferio re di
u n ’u n ità risp etto all’o r ig in a le e in v o c a ten e il m e to d o getArea.
^ E l 2 . 4 . S criv ete un m e to d o r ico rsiv o c h e costru isca una stringa c o n te n e n te le cifre b in arie di un
n u m er o in tero n . S e n è pari, allora l’ultim a cifra è 0; se n è dispari, l’ultim a cifra è 1. C a lc o la te
r ic o rsiv a m en te le altre cifre.
** E 1 2 . 8 . U sa te la r ic o rsio n e p er realizzare il m e to d o
676 C a p it o l o 12
E l 2 .9 . U sa te la r ic o r s io n e p er realizzare il m e to d o
c h e restitu isce la p o s iz io n e in izia le della prim a sotto strin g a di text c h e sia u gu ale alla stringa str.
R e s titu ite —1 se str n o n è una so tto strin g a di text. A d e se m p io , indexOf("Mississippi", "sip”)
restitu isce 6.
S u g g e r im e n to : q u esto è u n p o ’ p iù d ifficile d el p rob lem a p r e ce d e n te , p erch é d o v e te ten ere traccia di
q u a n to sia lon tan a d all’in iz io della frase la c o rr isp o n d e n z a c h e state cercan d o; in serite tale valore
c o m e param etro di u n m e to d o ausiliario.
rum , um ,
R ic o r s io n e 677
O sservate c h e n o n è n ecessa rio c h e i so tto in s ie m i sian o so tto strin g h e: ad ese m p io , "rm" n o n è una
so tto strin g a di "rum".
[[1], [7], [2], [9]], [[1, 7], [2], [9]], [[1], [7, 2], [9]], [[l, 7, 2], [9]],
[[1], [7], [2, 9]], [[1, 7], [2, 9]], [[1], [7, 2, 9]], [[l, 7, 2, 9]]
S u g g e r im e n to : p er prim a cosa g e n era te tutti i s o tto -v e tto r i del v etto re p rivato d e ll’u ltim o e le m e n to ,
c h e , p o i, p u ò essere a g g iu n to in fo n d o all’u ltim o s o tto -v e tto r e o p p u re p u ò costitu ire un s o tto
v etto re di lu n g h e zz a un itaria.
E 1 2 . 2 2 . L’a lg o r itm o di b ack tra ck in g fu n zio n a per qualsiasi p rob lem a le cu i s o lu z io n i parziali
p o ssa n o essere valutate ed estese. D e fin ite un tip o in terfaccia, PartialSolution, d o ta to d ei m e to d i
examine e extend; p o i, realizzate u n m e to d o , solve, c h e ela b o ri o g g e tti di tip o PartialSolution e una
classe, EightQueensPartialSolution, c h e im p le m e n ti l’in terfaccia.
Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
O rdinamento e ricerca 723
e. n + 0.001«'^
f. r? - 1000«^ + 10"
g- n + log (»)
h. + n log (n)
i. 2” +
j. («-' + 2n)/(n" + 0.75)
R13.4. Abbiamo calcolato che il numero effettivo di visite richieste dall’algoritmo di ordinamento
per selezione è
T{n) = (l/2)«2 + (5/2)«- 3
Abbiamo poi stabilito che questo metodo è caratterizzato da una crescita 0{ti-). Calcolate i rapporti
effettivi
T(2000)/T(IOOO)
T(4000)/T(IOOO)
T(IOOOO)/T(IOOO)
* R13.5. Supponiamo che l’algoritmo A impieghi 5 secondi per elaborare un insieme di 1000 dati.
Se l’algoritmo A ha prestazioni 0(«), quanto tempo impiegherà approssimativamente per elaborare
un insieme di 2000 dati? E uno di 10000?
** R13.6. Supponiamo che un algoritmo impieghi 5 secondi per elaborare un insieme di 1000 dati.
Riempite la tabella seguente, che mostra approssimativamente la crescita del tempo di esecuzione
in funzione della complessità dell’algoritmo.
Per esempio, dal momento che 3000^/1000^ = 9, se l’algoritmo fosse 0{ri-), per elaborare un
insieme di 3000 dati impiegherebbe un tempo 9 volte superiore a quello necessario per elaborare
un insieme di 1000 dati, cioè 45 secondi.
R13.7. Ordinate le seguenti espressioni O-grande in ordine crescente.
0(n)
0{n^)
0{rv‘)
0 ( l o g ( h ))
o(,r- log (»))
0 (ii log ( i l ) )
0 ( 2-0
O rdinamento e ricerca 725
0{n\fn)
0(rt*08(”))
R13.8.Qual è Tandamento del tempo di esecuzione dell’algoritmo standard che trova il valore
minimo in un array? E di quello che trova sia il minimo che il massimo?
R13.9. Qual è l’andamento del tempo di esecuzione del seguente metodo, in funzione di la
lunghezza di a? Visualizzate il risultato usando il “ metodo delle lampadine” visto nel Paragrafo 13.7.
public static void swap(int[] a)
{
int i = 0;
int j = a .length - l;
while (i < j)
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
}
}
R 1 3 . 1 0 . Una “ serie” {run) è una sequenza di valori adiacenti ripetuti (si veda l’Esercizio R7.23).
Descrivete un algoritmo 0 { n ) che trovi la lunghezza della serie più lunga in un array.
a. 4, 7, 11, 4, 9, 5, 11, 7, 3, 5
b. - 7 , 6, 8, 7, 5, 9, 0, 11, 10, 5, 8
a. 5, 11, 7, 3, 5, 4, 7, 11, 4, 9
b. 9, 0, 11, 10, 5, 8, -7, 6, 8, 7, 5
c. Ricerca binaria di 8 in - 7 , 1 , 2 , 3 , 5, 7, 1 0 , 13
726 C apitolo 13
R13.15. Il vostro compito consiste nel togliere tutti i duplicati da un array. Per esempio, se !’array
contiene i valori
4 7 11 4 9 5 11 7 3 5
Ecco un semplice algoritmo per risolvere il problema. Esaminate a[i] e contate quante volte ricorre
in a: se il conteggio è maggiore di 1, eliminatelo. Qual è l’andamento del tempo di esecuzione di
questo algoritmo?
R13.16. Modificate l’algoritmo di ordinamento per fusione in modo che elimini gli elementi
duplicati durante la fase di fusione, ottenendo così un algoritmo che elimina gli elementi duplicati
da un array. Si osservi che, alla fine, !’array non ha necessariamente lo stesso ordinamento dell’array
originario. Qual è l’efficienza di questo algoritmo?
R l3.17. Considerate il seguente algoritmo che elimina tutti i duplicati da un array. Ordinate !’array;
poi, esaminando ciascuno dei suoi elementi, per vedere se è presente più di una volta esaminate
l’elemento seguente e, in caso affermativo, eliminatelo. Questo algoritmo è più veloce di quello
dell’Esercizio R 13.15?
R13.18. Mettete a punto un algoritmo 0 (m\og(n)) per eliminare i duplicati da un array nel caso
in cui !’array risultante debba avere lo stesso ordinamento di quello originale. Quando un valore
è presente più volte, devono essere eliminate tutte le sue occorrenze tranne la prima.
R13.19. Perché, quando !’array è già ordinato, l’algoritmo di ordinamento per inserimento è
significativamente più veloce dell’ordinamento per selezione?
R13.20. Considerate la seguente modifica,che migliora le prestazioni dell’algoritmo di ordinamento
per inserimento visto nella sezione Argomenti avanzati 13.2: per ogni elemento dell’array, invocate
Arrays.binarySearch per determinare la posizione in cui va inserito. Questo miglioramento ha un
impatto significativo sull’efficienza dell’algoritmo?
R l3.21. Considerate il seguente algoritmo, noto come ordinamento a bolle {bubble sort):
Finché !'array non è ordinato
Per ogni coppia di elementi adiacenti
Se la coppia non è ordinata
Scambiai suoi elementi.
infatti, una sequenza di messaggi di posta elettronica {e-mait): se prima li ordinate in base alla data
e, poi, li ordinate di nuovo in base al nome del mittente, sarebbe davvero opportuno che la seconda
procedura di ordinamento preservasse l’ordine relativo tra gli elementi indotto dalla prima pro
cedura, in modo che l’utente possa vedere consecutivamente tutti i messaggi che hanno un certo
mittente, ordinati tra loro in base alla data. L’algoritmo di ordinamento per selezione è stabile? E
l’algoritmo di ordinamento per inserimento? Perché?
R13.24. Descrivete un algoritmo che in un tempo 0{n) ordini un array di n byte (cioè di numeri
compresi tra -128 e 127). Suggerimento: usate un array di contatori.
R l3.25. Dopo aver rappresentato le pagine di un libro con una sequenza di array di parole, do
vete costruire il relativo indice analitico (che è un array di parole ordinato), ciascun elemento del
quale è associato a un array ordinato di numeri: le pagine in cui tale parola compare. Descrivete
un algoritmo che costruisca tale indice e fate una stima O-grande del suo tempo di esecuzione
in funzione del numero totale di parole.
R13.26. Dati due array, ciascuno dei quali contiene tt numeri interi, descrivete un algoritmo 0{ti
\og{n)) che determini se hanno (almeno) un elemento in comune.
R13.27. Dato un array contenente n numeri interi e un valore v, descrivete un algoritmo 0{n
\og{n)) che determini se nell’array sono presenti due valori, x e y, la cui somma sia uguale a v.
R13.28. Dati due array, ciascuno dei quali contiene n numeri interi, descrivete un algoritmo 0{n
log(«)) che determini tutti gli elementi che hanno in comune.
R13.29. Immaginate di modificare l’algoritmo quicksort, descritto nella sezione Argomenti avan
zati 13.3, in modo che selezioni come pivot l’elemento centrale dell’array anziché il suo primo
elemento. Quali sono le prestazioni di questa variante dell’algoritmo quando viene eseguito su un
array già ordinato?
R13.30. Immaginate di modificare l’algoritmo quicksort, descritto nella sezione Argomenti avan
zati 13.3, in modo che selezioni come pivot l’elemento centrale dell’array anziché il suo primo
elemento. Individuate una sequenza di valori per il cui ordinamento questo algoritmo richieda
un tempo 0(rr).
E 13.1. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di
numeri interi in ordine decrescente.
E13.2. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di
monete in base al loro valore.
E13.3. Scrivete un programma che generi automaticamente la tabella dei tempi di esecuzione
dell’ordinamento per selezione. Il programma deve chiedere i valori minimo e massimo di n e il
numero di misurazioni da effettuare, per poi attivare tutte le esecuzioni.
E13.4. Modificate l’algoritmo di ordinamento per fusione in modo che ordini un array di stringhe
in ordine lessicografico.
E13.5. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di oggetti
che implementano l’interfaccia Measurable vista nel Capitolo 10.
E13.6. Modificate l’algoritmo di ordinamento per selezione in modo che ordini un array di oggetti
che implementano l’interfaccia Comparable (nella versione non generica, cioè senza parametro di tipo).
728 C apitolo 13
E13.7. Modificate Talgoritmo di ordinamento per selezione in modo che ordini un array di oggetti,
ricevendo un parametro di tipo Comparator (nella versione non generica,cioè senza parametro di tipo).
E13.8. Scrivete un programma per consultare l’elenco del telefono. Leggete un insieme di dati
contenente 1000 nomi e i relativi numeri di telefono, memorizzati in un file che contiene i dati in
ordine casuale. Gestite la ricerca sia in base al nome sia in base al numero di telefono, utilizzando
una ricerca binaria per entrambe le modalità di consultazione.
E13.9. Scrivete un programma che misuri le prestazioni dell’algoritmo di ordinamento per
inserimento descritto nella sezione Argomenti avanzati 13.2.
E13.10. Implementate !’algoritmo di ordinamento a bolle descritto nell’Esercizio R13.21.
E13.il. Implementate !’algoritmo descritto nel Paragrafo 13.7.4, tenendo però traccia solamente
del valore avente la frequenza più elevata fino a quel momento:
int mostFrequent = 0;
int highestFrequency = -l;
for (int i = 0; i < a .length; i++)
Conta le occorrenze di a[i] ina [i + i] . . . a[n - i]
Se ricorre più di highestFrequency volte
highestFrequency = Quelconteggio
mostFrequent = a [i]
Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
770 C apitolo 14
Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
R 1 4 .6 .
concatenata dopo ciascun passo.
LinkedList<String> sta ff = new LinkedList<>();
s t a f f . addFirst( "Harry");
staff.addFirst("Diana");
s t a f f . addFirst( "Tom");
System. out. println(s t a f f . removeLast( ) ) ;
System. out. println(s t a f f . removeFirst());
System. out. println(s t a f f . removeLast( ) ) ;
Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
R 1 4 .7 .
concatenata dopo ciascun passo.
LinkedList<String> sta ff = new LinkedListo();
s t a f f . addFirst( "Harry");
staff.addLast("Diana");
staff.addFirst("Tom");
System. out. println(s t a f f . removeLast( ) ) ;
System. out. println(s t a f f . removeFirst());
System. out. println( s t a f f . removeLast( ) ) ;
Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
R 1 4 .8 .
concatenata e la posizione dell’iteratore dopo ciascun passo.
LinkedList<String> sta ff = new LinkedList<>();
ListIterator<String> iterator = s t a f f . l i s t Iterator();
iterator. add( "Tom") ;
iterator. add( "Diana") ;
iterator.add("Harry");
Ja v a C ollections Fr a m e w o r k 771
* R14.9. Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
concatenata e la posizione dell’iteratore dopo ciascun passo.
LinkedList<String> sta ff = new LinkedList<>();
ListIterator<String> iterator = s ta ff.lis tI te r a to r ();
iterator. add( "Tom") ;
iterator. add( "Diana") ;
iterator. add( "Harry") ;
iterator = s ta ff.listI te r a to r ();
iterator.nextO ;
iterator.nextO ;
iterator. add( "Romeo") ;
iterator.nextO ;
iterator. add( "Du lie t") ;
iterator = s ta ff.listI te r a to r ();
iterator.nextO ;
iterator. removeO;
while (iterator.hasNextO ) { System.out.print In (iterator.nextO ); }
R14.10. Data una lista concatenata di stringhe, come vi si eliminano tutti gli elementi che hanno
lunghezza non superiore a tre?
R14.il (per Java 8). Risolvete nuovamente l’esercizio precedente usando il metodo removelf,
dopo averne letto la descrizione nella documentazione API dell’interfaccia Collection. Usate
un’espressione lambda (descritta nella sezione Note per Java 8 10.4).
R14.12. Quali vantaggi e svantaggi hanno le liste concatenate rispetto agli array?
R14.13. Supponete di dover organizzare un elenco di numeri di telefono per un reparto di una
società. Al momento vi sono circa 6000 dipendenti, sapete che il centralino può gestire al massimo
10000 numeri di telefono e prevedete che l’elenco venga consultato diverse centinaia di volte al
giorno. Per memorizzare le informazioni usereste un vettore o una lista concatenata?
R14.14. Immaginate di dover gestire un elenco di appuntamenti. Usereste una lista concatenata
O un vettore di oggetti di tipo Appointment?
* R14.15. Supponete di scrivere un programma che simula un mazzo di carte. Le carte vengono
pescate dalla cima del mazzo e distribuite ai giocatori. Quando le carte tornano nel mazzo, vengono
poste al di sotto del mazzo stesso. Memorizzereste le carte in una pila o in una coda?
^ R14.16. Ipotizzate che le stringhe "A" ... "Z" vengano inserite in una pila. Successivamente,
vengono estratte dalla pila e inserite in una seconda pila. Infine, vengono estratte dalla seconda
pila e visualizzate. In quale ordine?
* R14.17. Qual è la differenza fra un insieme e una mappa?
R14.18. L’unione di due insiemi ^4 e B è l’insieme di tutti gli elementi che sono contenuti in A,
in B O in entrambi. L’intersezione è , invece, l’insieme di tutti gli elementi che sono contenuti sia
in A sia in B. Come si possono calcolare l’unione e l’intersezione di due insiemi, usando i metodi
add e contains, oltre a un iteratore?
R14.19. Come si possono calcolare l’unione e l’intersezione di due insiemi usando i metodi
forniti dall’interfaccia ja v a .u til.S e t, ma senza usare un iteratore? Consultate la documentazione
API dell’interfaccia nella libreria standard.
772 C apitolo 14
R l4.20. Una mappa può avere due chiavi associate allo stesso valore? E due valori associati alla
stessa chiave?
R14.21. Una mappa può essere realizzata mediante un insieme di coppie {chiave, valore). Date una
spiegazione.
R14.22 (per Java 8). Come si possono visualizzare tutte le coppie chiave/valore presenti in una
mappa usando il metodo keySet? E usando il metodo entrySet? E il metodo forEach fornendo
un’espressione lambda (descritta nella sezione Note per Java 8 10.4)?
R14.23. Verificate che il codice di hash della stringa "Duliet" sia quello riportato nella Tabella 6.
R14.24. Verificate che le stringhe "VII" e "Ugh" abbiano lo stesso codice di hash.
R14.25. Considerate l’algoritmo di uscita da un labirinto visto nel Paragrafo 14.6.4,immaginando
di partire dalla posizione A e di inserire nella pila le direzioni nell’ordine ovest, sud, est e nord. In
quale ordine vengono visitate le posizioni indicate dalle lettere nel labirinto qui rappresentato?
R14.26. Ripetete l’esercizio precedente, usando una coda invece di una pila.
Carl: B+
Ioe : C
Sarah: A
E14.5. Scrivete un programma che legga un file di codice sorgente Java e generi un elenco di
tutti gli identificatori presenti, visualizzando, accanto a ciascuno di essi, i numeri delle righe in
cui compare. Per semplicità considereremo che qualsiasi stringa costituita soltanto da lettere, cifre
numeriche e caratteri di sottolineatura sia un identificatore. Dichiarate la variabile Scanner in per
leggere il file e invocate il metodo in.useDeliiniter("[''A-Za-zO-9_]+"),in modo che ogni invocazione
di next restituisca un identificatore.
E14.6 (per Java 8). Leggete da un file tutte le parole presenti e aggiungetele a una mappa
le cui chiavi siano le lettere iniziali delle parole e i cui valori siano insiemi contenenti le
parole che iniziano con quella stessa lettera. Quindi, visualizzate gli insiemi di parole in ordine
alfabetico.
Risolvete l’esercizio in due modi, uno che usi il metodo merge (descritto nella sezione Note
per Java 8 14.1) e uno che aggiorni la mappa come nella sezione Esempi completi 14.1.
E14.7 (per Java 8). Leggete da un file tutte le parole presenti e aggiungetele a una mappa le cui
chiavi siano le lunghezze delle parole e i cui valori siano stringhe composte da parole separate
da virgole, con parole aventi tutte la stessa lunghezza. Quindi, visualizzate tali stringhe in ordine
crescente di lunghezza delle loro singole parole componenti.
Risolvete l’esercizio in due modi, uno che usi il metodo merge (descritto nella sezione Note
per Java 8 14.1) e uno che aggiorni la mappa come nella sezione Esempi completi 14.1.
E14.8. Usate una pila per invertire le parole di una frase. Continuate a leggere parole, aggiungendole
alla pila, fin quando non trovate una parola che termina con un punto. A questo punto estraete
tutte le parole dalla pila e visualizzatele, poi ripetete la procedura fino all’esaurimento dei dati in
ingresso. Ad esempio, questa frase
Mary had a l i t t l e lamb. Its fleece was white as snow
Fate attenzione alle lettere maiuscole e al posizionamento del punto che termina la frase.
E l4.9. Dovete scomporre nu numero intero nelle sue singole cifre, trasformando, ad esempio, il
numero 1729 nella sequenza di cifre 1,7, 2 e 9. L’ultima cifra del numero n si ottiene facilmente
calcolando n %io, ma procedendo in questo modo si ottengono le cifre in ordine inverso. Risolvete
il problema usando una pila. 11 programma deve chiedere all’utente di fornire un numero intero,
per poi visualizzarne le singole cifre separate da spazi.
E14.10. In occasione di manifestazioni particolari, il proprietario di una casa noleggia posti auto
nel suo vialetto di casa, che può essere rappresentato da una pila, con il consueto comportamento
“last-in, first-out”. Quando il proprietario di un’automobile se ne va e la sua automobile non è
l’ultima, tutte quelle che la bloccano devono essere spostate temporaneamente sulla strada, per poi
rientrare nel vialetto. Scrivete un programma che simuli questo comportamento, usando una pila per
il vialetto e una per la strada, con numeri interi a rappresentare le targhe delle automobili. Un numero
positivo inserisce un’automobile nel vialetto, un numero negativo la fa uscire definitivamente e il
numero zero termina la simulazione.Visualizzate il contenuto del vialetto al termine di ciascuna
operazione.
774 C apitolo 14
E l4.11. Dovete realizzare un “elenco di cose da fare” {to do lisi).A ciascun compito viene assegnata
una priorità, un numero intero da 1 a 9, e una descrizione. Quando l’utente digita il comando add
priorità descrizione il programma aggiunge una cosa da fare, mentre quando l’utente digita next
il programma elimina e visualizza la cosa da fare più urgentemente. 11 comando quit termina il
programma. Risolvete il problema usando una coda prioritaria.
E14.12. Scrivete un programma che legga un testo da un file e lo suddivida in singole parole. Inserite
le parole in un insieme realizzato mediante un albero. Dopo aver letto tutti i dati, visualizzate tutte
le parole, seguite dalla dimensione dell’insieme risultante. Questo programma determina, quindi,
quante parole diverse sono presenti in un testo.
E14.13. Leggendo tutte le parole di un file di testo di grandi dimensioni (come il romanzo “War
and Peace”, Guerra e d i s p o n i b i l e in Internet), inseritele in due insiemi, uno realizzato mediante
tabella hash e uno realizzato mediante albero. Misurate i tempi di esecuzione: quale struttura agisce
più velocemente?
E14.14. Realizzate, nella classe BankAccount del Capitolo 8, metodi hashCode e equals che siano fra
loro compatibili.Verificate la correttezza dell’implementazione del metodo hashCode visualizzando
codici di hash e aggiungendo oggetti BankAccount a un insieme realizzato con tabella hash.
E14.15. Un punto geometrico dotato di etichetta è caratterizzato dalle coordinate x e y, oltre
che dall’etichetta, sotto forma di stringa. Progettate la classe LabeledPoint dotata del costruttore
LabeledPoint(int x, in t y, String lab el) e dei metodi hashCode e equals: due punti sono considerati
uguali quando si trovano nella stessa posizione e hanno la stessa etichetta.
E14.16. Realizzate una diversa versione della classe LabeledPoint vista nell’esercizio precedente,
memorizzando la posizione del punto in un oggetto di tipo java.aw t.Point. I metodi hashCode e
equals devono invocare gli omonimi metodi della classe Point.
E14.17. Modificate la classe LabeledPoint dell’Esercizio E14.15 in modo che implementi l’interfaccia
Comparable. Fate in modo che i punti vengano ordinati innanzitutto in base alla loro coordinata
x; se due punti hanno la stessa coordinata x, ordinateli in base alla loro coordinata y; se due punti
hanno le stesse coordinate, ordinateli in base alla loro etichetta. Scrivete un programma di collaudo
che verifichi tutti i casi, inserendo punti in un TreeSet.
E14.18. Aggiungete al valutatore di espressioni visto nel Paragrafo 14.6.3 l’operatore %,che calcola
il resto della divisione intera.
E14.19. Aggiungete al valutatore di espressioni visto nel Paragrafo 14.6.3 l’operatore '',che effettua
l’elevamento a potenza. Ad esempio, 2 ^ 3 ha come risultato 8. Come in matematica, l’elevamento
a potenza deve essere valutato da destra verso sinistra, cioè 2 ^ 3 ^ 2 è uguale a 2 ^ (3 ^ 2) e non
a (2 ^ 3) ^ 2 (quest’ultima quantità si può calcolare come 2 ^ (3 x 2)).
E14.20. Scrivete un programma che verifichi se una sequenza di marcatori HTML è annidata
correttamente. Per ogni marcatore di apertura, come <p>, ci deve essere un marcatore di chiusura,
</p>. All’interno di una coppia di marcatori, come <p> . . . </p>, possono essere presenti altri
marcatori, come in questo esempio:
<p> <ul> <li> </li> </ul> <a> </a> </p>
I marcatori più interni deve essere racchiusi tra quelli più esterni. Il programma deve elaborare un
file contenente marcatori: per semplicità, ipotizzate che i marcatori siano separati da spazi e che
al loro interno non ci sia altro testo, ma solo altri marcatori.
Ja v a C ollections Fr am ew or k 775
E14.21. Modificate il solutore di labirinti visto nel Paragrafo 14.6.4 in modo che possa gestire
anche labirinti con cicli. Utilizzate un insieme di incroci visitati: quando arrivate in un incrocio
già visitato in precedenza, trattatelo come un vicolo cieco e non aggiungete alla pila le sue
alternative.
E14.22. In un programma per disegnare, l’operazione di “riempimento per inondazione’’{floodfili)
assegna un determinato colore a tutti i pixel vuoti di un disegno, fermandosi quando raggiunge pixel
occupati. In questo esercizio svilupperete una semplice variante di questo algoritmo, riempiendo
per inondazione un array di numeri interi di dimensione 10 x 10, inizialmente riempito di
zeri. Chiedete all’utente la posizione iniziale: riga e colonna. Inserite in una pila la coppia <riga,
colonna> (vi servirà una semplice classe Pair).
Quindi, ripetete queste operazioni finché la pila non è vuota.
• La coppia <riga, colonna> presente in cima alla pila viene estratta.
• Se la posizione corrispondente è ancora vuota, riempitela, usando via via i numeri 1, 2,3, ecc.,
in modo da visualizzare l’ordine di riempimento delle celle.
• Le coordinate delle celle adiacenti in direzione nord, est, sud e ovest, che non siano state riem
pite, vengono inserite nella pila.
private static class Node { public E data; public Node next; } // ERRORE
}
Nel caso di variabili statiche, questa limitazione è molto stringente. Dopo la cancellazione
dei tipi generici, esiste un’unica variabile, LinkedList.defaultValue, mentre la dichiarazione
della variabile statica lascerebbe falsamente intendere che ne esista una diversa per ogni
diverso tipo di LinkedList<E>.
Per i metodi statici e le classi interne statiche esiste una semplice alternativa: aggiun-
gere un tipo parametrico.
public class LinkedList<E>
{
...
public static <T> List<T> replicate(T value, int n) {...} // va bene
private static class Node<T> { public T data; public Node<T> next; }
// va bene
}
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
* R15.2. Qual è la differenza tra una classe generica e una classe ordinaria?
* R15.3. Qual è la differenza tra una classe generica e un metodo generico?
* R15.4. Nella libreria standard di Java, identificate un esempio di metodo generico non statico.
** R15.5. Nella libreria standard di Java, identificate quattro esempi di classi generiche che abbiano
due tipi parametrici.
** R15.6. Nella libreria standard di Java, identificate un esempio di classe generica che non sia una
delle classi che realizzano contenitori.
* R15.7. Perché nel metodo seguente serve un vincolo per il tipo parametrico, T?
<T extends Comparable> int binarySearch(T[] a, T key)
** R15.8. Perché nella classe HashSet<E> non serve un vincolo per il tipo parametrico, E?
* R15.9. Che cosa rappresenta un esemplare di ArrayList<Pair<T, T>>?
** R15.10. Illustrate i vincoli applicati ai tipi nel seguente metodo della classe collections:
Perché non è sufficiente scrivere <T extends Comparable> oppure <T extends Comparable<T>>?
Esercizi di programmazione
* E15.1. Modificate la classe generica Pair in modo che i due valori siano dello stesso tipo.
* E15.2. Aggiungete alla classe Pair dell’esercizio precedente un metodo swap che scambi tra loro
il primo e il secondo elemento della coppia.
** E15.3. Realizzate un metodo statico generico PairUtil.swap, il cui parametro sia un oggetto di
tipo Pair, usando la classe generica definita nel Paragrafo 15.2. Il metodo deve restituire una nuova
coppia avente il primo ed il secondo elemento scambiati rispetto alla coppia originaria.
** E15.4. Scrivete un metodo statico generico PairUtil.minmax che identifichi gli elementi minimo
e massimo presenti in un array di tipo T e restituisca una coppia contenente tali valori minimo e
massimo. Esprimete il fatto che gli elementi dell’array devono implementare l’interfaccia Measurable
vista nel Capitolo 10.
** E15.5. Risolvete nuovamente il problema dell’esercizio precedente, richiedendo però che gli
elementi dell’array implementino l’interfaccia Comparable.
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore
*** E15.6. Risolvete nuovamente il problema dell’Eesercizio E15.4, richiedendo però che il tipo
parametrico estenda il tipo generico Comparable.
** E15.7. Realizzate una versione generica dell’algoritmo di ricerca binaria.
*** E15.8. Realizzate una versione generica dell’algoritmo di ordinamento per fusione. Il programma
deve essere compilato senza che vengano segnalati warning.
*** E15.9. Dotate di un appropriato metodo hashCode la classe Pair vista nel Paragrafo 15.2 e realizzate
una classe HashMap che usi un esemplare di HashSet<Pair<K, V>>.
*** E15.10. Realizzate una versione generica del generatore di permutazioni visto nel Paragrafo 12.4,
in modo che generi tutte le permutazioni degli elementi presenti in un esemplare di List<E>.
** E15.11. Scrivete un metodo statico generico, print, che visualizzi l’elenco degli elementi pre-
senti in qualsiasi esemplare di una classe che implementi l’interfaccia Iterable<E>, inserendolo in
un’appropriata classe di utilità. Gli elementi visualizzati devono essere separati da una virgola.
3. Il primo contiene coppie, ad esempio [(Tom, 1), (Harry, 3)], mentre il secondo contiene un elenco
di stringhe e un unico numero intero, ad esempio ([Tom, Harry], 1).
4. Ciò che viene visualizzato dipende dalla definizione del metodo toString nella classe BankAccount.
5. No, il metodo non ha tipi parametrici, si tratta di un metodo ordinario all’interno di una classe
generica.
6. public class TreeSet <E extends Comparable>
9. La classe supera la compilazione (con un warning), ma si tratta di una tecnica debole e se, in futuro,
non si effettuerà più la cancellazione dei tipi parametrici, questo codice sarà errato: il cast da Object[]
a String[] provocherà il lancio di un’eccezione.
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore