Il 0% ha trovato utile questo documento (0 voti)
220 visualizzazioni

Esercizi Java

Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
220 visualizzazioni

Esercizi Java

Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 450

28 C apitolo I

Catalogazione degli errori: errori di sintassi ed errori logici


• U n errore di sintassi è un a v io la z io n e d e lle r eg o le d el lin g u a g g io di p r o g r a m m a z io n e id e n ­
tificata dal c o m p ila to re.
• Si ha u n errore lo g ic o q u a n d o u n p rogram m a e se g u e u n ’a z io n e n o n prevista dal p ro g ra m ­
m atore.

Scrittura di semplici algoritmi mediante pseudocodice


• U n a lg o r itm o c h e r iso lv e un p rob lem a è u n a seq u en za di passi n o n a m b igu a, e se g u ib ile e c h e
term in a in un te m p o fin ito.
• L o p s e u d o c o d ic e è u n a d e s c r iz io n e in fo r m a le di u n a se q u e n z a di passi c h e r is o lv o n o u n
p rob lem a.

ll^lTilipti di libreria presentati nel capitolo


java.io.PrintStream
print
println
java.lang.System
out

Esercizi di riepi|Ms.gjiiffi8fcadllli?nIft fjtìl'


R l . l . S p ieg a te la d ifferen za c h e in terco rre fra usare u n p rogram m a al ca lco la to re e p rogram m are
u n co m p u ter.

R I . 2 . Q u a li parti di u n c o m p u te r p o s so n o c o n te n e r e il c o d ic e di un program m a? E quali i dati


da elaborare o elaborati?

R I . 3 . Q u a li parti di u n c o m p u te r h a n n o il c o m p ito di forn ire in fo r m a z io n i all’uten te? E quali


a c q u isisco n o dati fo rn iti dall’uten te?

R I . 4 . U n tostap an e è un d isp o sitiv o aven te u n ’u n ica fu n z io n e , m en tre u n c o m p u te r p u ò essere


p ro gram m ato per svo lg ere m o lti c o m p iti diversi. Il vostro te le fo n o cellu lare è un d isp o sitiv o ch e
ha u n ’u n ica fu n z io n e o p p u re è un c o m p u te r p rogram m ab ile? La risposta d ip e n d e dal m o d e llo di
te le fo n o .

R I . 5 . Illustrate d u e van taggi d erivan ti d all’u so di c o d ic e Java risp etto al c o d ic e m a cch in a .

R I . 6 . N e l vostro c o m p u te r p erson ale, op p u re in u n o di q u elli del lab o ra to rio di in form atica,


trovate la p o s iz io n e esatta (n o m e della cartella):

a. del file di e se m p io HelloPrinter. java c h e avete scritto c o n !’editor;


b. d e ll’e secu to re di p rogram m i Java, java.exe op p u re se m p lic e m e n te java;
c. del file rt. jar, c h e c o n tie n e la libreria di e se c u z io n e {mn-time library).
R I . 7 . C o sa visu alizza q u esto program m a?

public class Test


{
public static void main(String[] args)
{
System.out.printIn("3 9 + 3 " ) ;
System.out.printIn(39 + 3 );
}
}
Introduzione 29

R I . 8 . C o sa visu alizza q u esto p rogram m a? Fate a tte n z io n e alle spaziature.

public class Test


{
public static void main(String[] args)
{
System.out.print("Hello");
System.out.printIn("World");
}
}
R I . 9. Q u a le errore di sintassi è p resen te in q u esto program m a?

public class Test


{
public static void main(String[] args)
{
System.out.println("Hello", "World!");
}
}
R I . 10. S crivete tre version i del program m a HelloPrinter. java,c o n errori di sintassi diversi. S criveten e
un a v e rsio n e c o n u n errore lo g ic o .

^ R l . l l . In c h e m o d o si s c o p r o n o gli errori di sintassi? E gli errori lo g ici?

R I . 12. La m en sa d e ll’u n iversità v e n d e ai su o i c lie n ti una tessera c h e dà d ir itto a un pasto gratis


o g n i volta c h e , du ran te u n d e te rm in a to p e r io d o di te m p o , si s o n o acquistati un c e r to n u m ero
di pasti. I d ettagli deH’ofFerta s o n o variab ili n el te m p o . D e sc r iv e te u n a lg o r itm o c h e c o n se n ta di
d eterm in are se una particolare offerta sia e c o n o m ic a m e n te van taggiosa. D i quali dati avete b isogn o?

** 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 ?

kirir R I. 14. C o n sid e ra te di n u o v o l ’ese rc iz io p r e ce d e n te e s u p p o n e te c h e i valori c h e v i figu ran o


($ 1 0 0 0 0 ,6 % , $ 5 0 0 ) siano a d iscrezio n e d e ll’u te n te del program m a. E sisto n o valori c h e im p e d isc o n o
all’a lg o r itm o c h e avete sv ilu p p a to di term inare? In caso afferm ativo, m o d ific a te l ’a lg o r itm o in
m o d o da avere la certezza c h e te rm in i sem pre.

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?

** R I . 17. Im m a g in a te di v o ler affidare al vostro fratello m in o r e il c o m p ito di effettuare la co p ia


di sicu rezza ( b a c k u p ) d el vostro lavoro. S criv ete u n in sie m e di istru zio n i dettagliate p er sv olgere
tale c o m p ito , sp ie g a n d o q u a n to sp esso o cco r ra farlo e quali file d e b b a n o essere co p ia ti, in d ic a n d o
e sp licita m en te la cartella d ’o r ig in e e qu ella di d e stin a z io n e d e ll’a z io n e di copiatura. Sp iegate, in fin e,
in c h e m o d o d eb b a essere verificata la corretta e s e c u z io n e d el b acku p.
30 C apitolo I

R I . 1 8 . S criv ete lo p s e u d o c o d ic e di u n a lg o ritm o c h e d escriva la p rep arazion e della c o la z io n e


d o m e n ic a le a casa vostra.

R I . 1 9 . G li a n tic h i B a b ilo n e si c o n o s c e v a n o u n a lg o r itm o p er ca lco la re la ra d ice quadrata d el


n u m e r o a . Si parte c o n u n v alore in iz ia le ,^ = a / 2 , p o i si ca lcola il v alore m e d io tra e a /g , che
sarà il n u o v o valore di g ; si r ip ete fin c h é d u e v a lo ri c o n s e c u tiv i assunti da g d iffe r is c o n o di un a
q u an tità desid erata e a q u e l p u n to g sarà il v alore cerca to . S c r iv e te lo p s e u d o c o d ic e p er q u e s to
a lg o r itm o .

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 .2 . S criv ete un p rogram m a c h e c a lc o li e visu alizzi la so m m a d ei p rim i 10 n u m er i in teri p ositivi:


1 + 2 + ... + 10.
E 1 .3 . S criv ete un program m a c h e ca lco li e visualizzi il p r o d o tto dei p rim i 10 n u m eri in teri positivi:
1 X 2 X . . . X 1 0 (in Java, la m o ltip lic a z io n e si in d ica c o n l’asterisco, *).

E 1 .4 . S criv ete un p rogram m a c h e c a lc o li e visu alizzi il saldo di un c o n to b a n cario d o p o il p rim o ,


s e c o n d o e terzo ann o. Il c o n to ha u n saldo in izia le di $ 1 0 0 0 e vi v e n g o n o accred itati a n n u a lm en te
interessi pari al 5% d el saldo.

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

★ ★ ★ E 1 .6 . S crivete un program m a c h e scriva il vostro n o m e c o n lettere m o lto grandi, c o m e n ell’e se m p io


seg u en te:

in
:«c 3 (c * 3(c H
c
♦♦ ♦♦ ♦ *

** E 1 .7 . S c r iv e te un p rogram m a c h e v isu a lizz i il vostro n o m e u san d o l ’alfab eto M o rse, c o m e


n e ll’e se m p io se g u e n te , u san d o un a diversa in v o c a z io n e di System.o u t.p r in t per ciascu n a lettera:

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.

E l . l O . S criv ete u n p rogram m a c h e visu alizzi un a casa, id e n tica a questa:

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 3 . S criv ete un p rogram m a c h e visu alizzi u n a p oesia di vostro g r a d im en to . Se n o n n e avete


una preferita, cercate in In tern et “ E m ily D ic k in s o n ” o “ e e c u m m in g s ” .

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 =.

E 1 .1 5 . C o p ia te ed e se g u ite il p rogram m a se g u e n te , p o i m o d ific a te lo in m o d o c h e visu alizzi il


m e ssa g g io “ H e llo , vostro nomeV\
import javax.swing.lOptionPane;

public class DialogViewer


{
public static void main(String[] args)
{
lOptionPane.showMessageDialog(null, "Hello, World!");
}
}
E 1 . 1 6 . C o p ia te ed e se g u ite il p rogram m a se g u e n te , p o i m o d ific a te lo in m o d o c h e visu alizzi il
m e ssa g g io “ H e llo , nomeV\ essen d o nome c iò c h e v ie n e scritto dall’u te n te nella finestra di d ia lo g o
c h e co m p a re du ran te l’e s e c u z io n e del program m a.

import javax.swing.lOptionPane;

public class DialogViewer


{
public static void main(String[] args)
{
String name = lOptionPane.showInputDialog("What is your name?”);
System.out.println(name);
}
}
32 C apitolo I

★★★ El. 17. M o d ific a te il p rogram m a d e ll’ese rc iz io p r e ce d e n te in m o d o c h e il d ia lo g o c o n l ’u te n te


p rosegu a c o n il m e ssa g g io “ M y n a m e is H a liW h a t w o u ld y o u lik e m e to d o ? ” D o p o d ic h é ig n o ra te
q u a n to scritto dall’u te n te e visu alizzate in o g n i caso il m e ssa g g io s e g u e n te (so stitu en d o Dave c o n
il n o m e fo r n ito in izia lm en te d all’u ten te):

I ’m sorry, Dave. I'm afraid I can't do that.

El. 18. C o p ia te ed e se g u ite il p rogram m a se g u e n te , p o i m o d ific a te lo in m o d o c h e visu alizzi un


d iverso salu to e una diversa im m a g in e.

import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.DOptionPane;

public class Test


{
public static void main(String[] args) throws Exception
{
URL imageLocation = new URL(
"http://horstmann.com/java4 everyone/duke.gif");
DOptionPane.shov^essageDialog(null, "Hello", "Title",
lOptionPane.PLAINMESSAGE, new Imagelcon(imageLocation));
}
}
* El. 19 (economia). S criv ete u n p rogram m a c h e visu alizzi l ’e le n c o d e lle date d ei c o m p le a n n i d ei
vostri a m ici, su d u e c o lo n n e : nella p rim a c o lo n n a ci sian o i n o m i e nella seco n d a le date.

* 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:

Sales Tax Rates

Alaska: 0%
Hawaii: 4%

E 1 .2 1 (economia). C o n o sc e r e più di una lin gu a è u n ’abilità m o lto im p ortan te n e ll’attuale m ercato


del lavoro e un a d e lle attività p iù e le m en ta ri c o n siste n el p orgere un saluto. S criv ete u n program m a
c h e visu alizzi una tabella c o n d u e c o lo n n e , r ip o rta n d o frasi di saluto: nella prim a c o lo n n a la frase
in in g lese e nella se c o n d a la stessa frase in u n ’altra lin gu a, a vostra scelta. S e n o n parlate altre lin g u e
oltre all’in g lese, usate un traduttore in In tern et o c h ie d e te a u n a m ico .

Elenco di frasi da tradurre


G o o d m o r n in g .
It is a pleasure to m e e t y o u .
P lease call m e to m o rro w .
________ H ave a n ic e day!________

Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
U tilizzare oggetti 79

Programmi che visualizzano finestre


• Per visualizzare una finestra c o n c o r n ic e (fran te) si co stru isce un o g g e tto di tip o DFrame, se ne
im p o sta n o le d im e n sio n i e lo si ren d e visib ile.
• P er v isu a lizz a r e q u a lco sa in u n frain e, o c c o r r e d ich iarare u n a classe c h e e ste n d a la classe
DComponent.
• In serite le istru zio n i di d ise g n o aH’in te rn o del m e to d o paintComponent, c h e v ie n e in v o ca to o g n i
volta c h e il c o m p o n e n te d e v e essere rid isegn ato.
• Per o tten er e l’o g g e tto di tip o GraphicsZD a partire dal param etro di tip o Graphics del m e to d o
paintComponent b isogn a usare un cast.

Utilizzo deH'API di Java per disegnare semplici figure


• EllipseZD.Double e LineZD.Double so n o classi c h e d e sc r iv o n o fo r m e grafich e.
• Il m e to d o drawString disegn a una stringa a partire dal su o p u n to base.
• Q u a n d o im p o sta te un n u o v o c o lo r e n el c o n te sto grafico, verrà usato n elle o p era zio n i grafich e
su ccessive.

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

Esercizi dì riepilogp e approfouJigipoto.


R 2 . 1 . S p ieg a te la d ifferenza fra u n o g g e tto e una classe.

R 2 . 2 . F o rn ite tre esem p i di o g g e tti c h e a p p a rte n g o n o alla classe String. F o rn ite un e se m p io di


o g g e tto c h e ap p artien e alla classe PrintStream. F o rn ite il n o m e di d u e m e to d i c h e a p p a rten g o n o alla
classe String m a n o n alla classe PrintStream. F o rn ite il n o m e di un m e to d o della classe PrintStream
c h e n o n ap p artien e alla classe String.

R 2 . 3 . C o s ’è V in terfaccia p u b b lic a di una classe? In cosa d ifferisce dalla sua im p le m e n ta z io n e ?

R 2 . 4 . D ich ia ra te e in izializzate variabili c h e m e m o r iz z in o il p rezzo e la d e sc r iz io n e di u n a rtic o lo


p o sto in ven d ita.

R 2 . 5 . C h e valore ha mystery d o p o questa seq u en za di en u n ciati?

in t mystery = 1;
80 C apitolo 2

mystery = I - 2 * mystery;
mystery = mystery + l;

R 2 . 6 . C h e errore c ’è in questa seq u en za di en u n ciati?

int mystery = l;
mystery = mystery + 1;
int mystery = 1 - 2 * mystery;

R 2 .7 . Illustrate il sig n ifica to del sim b o lo = in Java e in m a te m a tic a .

R 2 . 8 . F o rn ite u n e se m p io di m e to d o c h e ricev e u n a r g o m e n to di tip o int. F o rn ite u n e se m p io


di m e to d o c h e restitu isce u n valore di tip o int. Fate le stesse c o s e c o n il tip o String.

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 0 . Scrivete en u n ciati Java c h e in izializzin 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 replace.

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 2 . S p ieg a te la differenza fra un o g g e tto e un a variab ile o g g e t to .

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.

R 2 . 1 4 . S criv ete e n u n cia ti Java p er co stru ire gli o g g e tti c o sì d e sc r itti:

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!".

C reate so lta n to gli o g g e tti, n o n le variabili o g g e tto .

R 2 . 1 5 . R ip e t e te l’ese rc iz io p r e ce d e n te , d e fin e n d o ora variab ili o g g e t t o c h e v e n g a n o in izializzate


c o n gli o g g e tti app en a co stru iti.

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 ).

R 2 . 1 8 . Id en tifica te gli errori p resen ti n e g li en u n cia ti se g u e n ti:

a. Rectangle r = (5, 10, 15, 20);


b. double width = Rectangle(5, 10, 15, 20).getWidth();
c. Rectangle r;
r.translate(l5, 25);
d. r = new Rectangle();
r.translate("far, far away!");

R 2 . 1 9 . In d icate d u e m e to d i d ’a ccesso e d u e m e to d i m o d ific a to r i della classe Rectangle.


U tilizzare oggetti 81

R 2 . 2 0 . C o n su lta te la d o c u m e n ta z io n e A P I p er scop rire m e to d i per

• 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.

Per la s o lu z io n e di ciascu n p rob lem a, citate il n o m e d el m e to d o id e n tific a to e la classe in cu i è


d e fin ito , il su o valore restitu ito e i tipi d ei su o i p aram etri.

R 2 . 2 1 . S p iegate la differenza fra u n o g g e tto e u n r ife r im e n to a u n o g g e tto .

R 2 .2 2 ( g r a f i c a ) . S p ieg a te la d ifferen za fra u n ’a p p lic a zio n e grafica e u n ’a p p lic a zio n e per c o n so le .

** 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?

** R 2 .2 4 ( g r a f i c a ) . P erch é il p aram etro d el m e to d o paintComponent è di tip o Graphics, a n z ich é


Craphics2D?

★★ R 2 .2 5 ( g r a f i c a ) . A cosa serve u n c o n te s to grafico?

★★ R 2 .2 6 ( g r a f i c a ) . P erch é n ei p rogram m i grafici u sia m o classi separate per i c o m p o n e n ti e p er la


loro v isu a lizza zio n e?

R 2 .2 7 ( g r a f i c a ) . In c h e m o d o si sp ecifica il c o lo r e del testo?

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.

E 2 .2 ( c o l l a u d o ) . S criv ete il p rogram m a PerimeterTester c h e costru isca un o g g e tto di tip o Rectan­


gle, n e ca lco li il p e r im e tro e lo visu alizzi. U sa te i m e to d i getWidth e getHeight e visu alizzate a n ch 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.

E 2 . 5 ( c o l l a u d o ) . C o n su lta te la d o c u m e n ta z io n e A P I della classe Rectangle e in d iv id u a te il m e to d o :

void add(int newx, int newy)

L e g g ete la d o c u m e n ta z io n e d el m e to d o , p o i d e te rm in a te il risu ltato p r o d o tto dagli en u n cia ti


seg u en ti:
Rectangle box = new Rectangle(5, 10, 20, 30);
box.add(0, O);
82 C apitolo 2

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".

E2.8 (collaudo). La classe StringBuilder ha un m e to d o reverse c h e in verte un a stringa. N e lla classe


ReverseTester, c o stru ite un o g g e tto di tip o StringBuilder u san d o c o m e param etro di c o str u z io n e
un a stringa data (c o m e "desserts"), p o i in v o c a te il m e to d o reverse se g u ito dal m e to d o toString e
visu alizzate il risultato p r o d o tto , oltre a q u e llo previsto.

E2.9. N e lla libreria Java, u n c o lo r e v ie n e sp ec ific a to m e d ia n te le su e tre c o m p o n e n ti (rosso, verd e


e b lu ), c o n valori n u m e r ic i c o m p resi tra 0 e 2 5 5 , c o m e v isto nella T abella 4. S criv ete il p rogram m a
BrighterDemo c h e costru isca u n o g g e tto di tip o Color c o n i valori di rosso, verd e e blu risp ettiva­
m e n te ugu ali a 5 0 , 1 0 0 e 1 5 0 . S u ccessiv a m en te, ap p licate il m e to d o brighter della classe Color e
visu alizzate i valori d e lle tre c o m p o n e n ti del c o lo r e risu ltan te (n o n ved rete v e ra m e n te il co lo re:
p er visualizzare i c o lo r i, sv o lg e te il p ro ssim o esercizio ).

E2.10 (grafica). R is o lv e te n u o v a m en te l ’ese rc iz io p r e ce d e n te , m a in serite il vostro c o d ic e nella


classe qu i riportata: in q u esto m o d o il c o lo r e verrà v isu alizzato v eram en te.

import java.awt.Color;
import javax.swing.DFrame;

public class BrighterDemo


{
public static void main(String[] args)
{
!Frame frame = new DFrame();
frame.setSize(200, 200);
Color myColor = ... ;
frame.getContentPane().setBackground(myColor);
frame.setDefaultCloseOperation(DFrame.EXIT_0N_CL0SE);
frame.setVisible(true);
}
}

E2.11. R is o lv e te n u o v a m en te l ’ese rc iz io E 2 .9 ,m a ap p licate d u e v o lte il m e to d o darker della classe


Color al c o lo r e p r e d e fm ito Color.RED. C h ia m a te il vostro p rogram m a DarkerDemo.

E 2 . 1 2 . La classe Random realizza u n g e n e r a to r e d i n u m e r i c a su a li, c io è g en era s e q u e n z e di n u m eri


c h e a p p a io n o essere casuali. Per gen erare n u m er i in te ri casuali b isogn a costru ire u n o g g e tto della
classe Random, a c u i applicare p o i il m e to d o nextInt.Ad e se m p io , l ’in v o c a z io n e generator.nextint(6)
fo r n isc e un n u m er o casuale c o m p r eso tra 0 e 5.
S criv ete il p rogram m a DieSimulator c h e usi la classe Random p er sim ulare il la n c io di u n dad o, v i­
su alizzan d o u n n u m er o casuale c o m p r e so tra 1 e 6 o g n i volta c h e v ie n e ese g u ito .

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.14 (collaudo). C o n su lta te la d o c u m e n ta z io n e A PI della classe Point e sc o p r ite c o m e si c o stru ­


isce u n o g g e tto di tip o Point. N e l p rogram m a PointTester, c o stru ite d u e p u n ti aventi co o rd in a te
(3, 4) e ( - 3 , - 4 ) , p o i ca lco la te la lo ro distanza u san d o il m e to d o distance.V isu alizzate la distanza
co sì calcolata, oltre a qu ella prevista (e v en tu a lm en te fa c en d o una figura su u n fo g lio di carta per
calcolarla).

E2.15. U sa n d o la classe Day vista nella se z io n e E sem p i c o m p le ti 2 . ! ,s c r iv e te il p rogram m a DayTester


c h e costru isca u n o g g e tto di tip o Day rappresentante la data o d ie rn a , a g g iu n g e te v i d ie ci g io r n i e
ca lco la te la differenza tra le d u e date, v isu a lizza n d o il risultato p r o d o tto in sie m e a q u e llo previsto.

E2.16. U s a n d o la classe Picture vista nella s e z io n e E sem p i c o m p le ti 2 .2 , scrivete il p rogram m a


HalfSizePicture c h e ca rich i una figura e la v isu alizzi c o n un a d im e n sio n e pari alla m età di qu ella
o r ig in a le , centrata aH’in te r n o della finestra.

E2.17. U s a n d o la classe Picture vista nella se z io n e E sem p i c o m p le ti 2 .2 , scrivete il p rogram m a


DoubleSizePicture c h e carich i una figura e la visu alizzi c o n una d im e n sio n e pari al d o p p io di q u ella
o r ig in a le , cen trata all’in te r n o della finestra.

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

• L’in c a p su la m e n to a g ev o la l ’in d iv id u a z io n e di errori e la m o d ific a di d ettagli realizzativi di


una classe.

Intestazioni di metodi e costruttori per descrivere !Interfaccia pubblica di una classe


• Per realizzare una classe, o c c o r r e p rim a in d ivid u are quali m e to d i sian o necessari.
• I c o stru tto ri im p o sta n o i valori in iziali p er i dati d eg li o g g e tti.
• Il n o m e di u n costru tto re è sem p re u g u a le al n o m e della classe.
• Per d escrivere le classi e i m e to d i p u b b lici d ei p rogram m i si u san o i c o m m e n ti di d o c u m e n ­
ta zio n e.
• S criv ete c o m m e n ti di d o c u m e n ta z io n e p er o g n i classe, o g n i m e to d o , o g n i param etro e o g n i
valore restitu ito.

Implementazione di una classe


• L’im p le m e n ta z io n e privata di u n a classe c o m p r e n d e le variab ili di e sem p la re e il c o r p o di
c o stru tto ri e m e to d i.

Progetto di un collaudo che verifichi il corretto funzionamento di una classe


• U n c o lla u d o di un ità verifica il c o rr etto fu n z io n a m e n to di un a classe a sé stante, senza c h e sia
in serita in u n p rogram m a c o m p le to .
• Per collau d are una classe si usa un a m b ie n te in terattivo o p p u re si scrive un a classe di test c h e
eseg u a istru zio n i di co lla u d o .

Tenere traccia dell'esecuzione per visualizzare il comportamento degli oggetti


• S cr iv e te i m e to d i su una faccia di un a sch ed a e le variabili di esem p lare su ll’altra faccia.
• Q u a n d o v ie n e in v o c a to u n m e to d o m o d ifica to r e, a g g io rn a te le variabili di esem p lare.

Confronto tra periodo vitale e inizializzazione di variabili di esemplare, locali e parametro


• Le variab ili lo ca li so n o d ich iarate n el c o r p o di u n m e to d o .
• Q u a n d o u n m e to d o term in a la propria e se c u z io n e , le su e variabili lo ca li sc o m p a io n o .
• Le variabili di esem p lare v e n g o n o in izializzate a un valore p red efin ito, m en tre le variabili locali
d e v o n o essere in izializzate e sp licita m en te.

Uso del parametro implicito nella dichiarazione di metodi


• L’uso, all’in te r n o di u n m e to d o , del n o m e di una variab ile di esem p lare rappresenta la variabile
di esem p lare del param etro im p lic ito .
• Il r ife r im e n to t h i s rappresenta il param etro im p lic ito .
• U n a variab ile lo ca le m e tte in om b ra un a variab ile di esem p lare c h e abbia lo stesso n o m e : si
p u ò a c ced ere a q u est’ultim a u san d o il r ife r im e n to t h i s .
• L’in v o c a z io n e di u n m e to d o sen za usare un param etro im p lic ito ag isce sul m e d e s im o o g g e tto
su c u i si sta o p era n d o .

Realizzazione di classi che disegnano forme complesse


• C reare un a classe p er o g n i p o r z io n e di figura c h e possa essere u tilizzata p iù v o lte è u n ’ottim a
idea.
• P er capire c o m e disegn are un a figura co m p lessa, fa ten e u n o sc h iz zo su carta m illim etrata.

Esercizi di^spijfiflp e approf(yu|i|]]£|)tg.


R 3 .1 . C o s ’è l ’in terfaccia p u b b lica di una classe? In cosa d ifferisce daH’im p le m e n ta z io n e della classe
stessa?
R ealizzare classi 133

R 3 . 2 . C o s ’è r in ca p su la m e n to ? P erch é è utile?

R 3 . 3 . L e variabili di esem p lare fa n n o parte d e lP im p le m e n ta z io n e privata di una classe, m a s o n o


v isib ili a q u ei p ro gram m atori c h e ab b ian o a d isp o siz io n e il c o d ic e so r g e n te della classe stessa.
S p ieg a te in c h e se n so la parola riservata private p ro v v ed e a “ n a sco n d e re ” l’in fo r m a zio n e .

R 3 . 4 . C o n sid e ra te un a classe Grade c h e rappresenti u n v o to , in lettere, c o m e A + o B, s e c o n d o le


c o n su e tu d in i anglosasson i. D e lin e a te d u e d iverse sc e lte per le variabili di esem p lare da usare p er
realizzarla.

R 3 . 5 . C o n sid e ra te una classe Time c h e rappresenti u n istante di te m p o , c o m e le 9 del m a ttin o (9


a .m ., n el m o n d o an glosasson e) o le 3 .3 0 d el p o m e r ig g io (3 .3 0 p .m .). D e lin e a te d u e diversi in sie m i
di variabili di esem p lare c h e si p o treb b ero usare per realizzarla.

R 3 . 6 . Im m a g in a te c h e il progettista della classe Time d e ll’e sercizio p r e ce d e n te passi da una strategia


di rea lizza zio n e all’altra, senza m o d ifica re l ’interfaccia p u b b lica. C osa d e v o n o fare i p rogram m atori
c h e u tiliz z a n o la classe Time?

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)?

R 3 . 9 . P erch é la classe BankAeeount n o n d isp o n e di u n m e to d o reset, c h e n e rip orti lo stato alle


c o n d iz io n i iniziali?

R 3 . 1 0 . C osa su cced e, nella nostra realizzazion e della classe BankAeeount, q u an d o da u n c o n to b an cario


v ie n e prelevata un a quantità di den aro m a g g io re d el saldo d isp o n ib ile in q u el m o m e n to ?

R 3 . 1 1 . C o s ’è il r ife r im e n to th is? Per qu ale m o tiv o lo usereste?

R 3 . 1 2 . Tra i m e to d i della classe CashRegister vista nella se z io n e E sem p i c o m p le ti 3 .1 , quali so n o


m e to d i d ’a ccesso e qu ali, in v e ce , m e to d i m o d ifica to r i?

R 3 . 1 3 . C o sa fa il m e to d o seg u en te? F o rn ite u n e se m p io di c o m e si p o treb b e in vocare il m e to d o .

public class BankAccount


{
public void mystery(BankAccount that, double amount)
{
this.balance = this.balance - amount;
that.balance = that.balance + amount;
}
. . . // gli altri metodi del conto bancario
}
R 3 . 1 4 . Im m a g in a te di v o le r realizzare la classe TimeDepositAccount c h e rap p resen ti u n c o n t o
b a n ca rio aven te u n tasso di in teresse attivo fisso, il cu i valore v ie n e d e te r m in a to al m o m e n to della
c o str u z io n e d e ll’o g g e tto , in sie m e al saldo in iziale. P rogettate u n m e to d o c h e forn isca il valore del
saldo. P rogettate, p o i, un m e to d o c h e accred iti sul c o n t o gli interessi m aturati, senza aver b iso g n o
di param etri, dal m o m e n to c h e il tasso di interesse è n o to , e sen za restituire a lcu n c h é , v isto c h e
134 C apitolo 3

esiste u n altro m e to d o c h e fo r n isc e il valore d el saldo. In u n c o n to di q u esto tip o n o n è p ossib ile


versare u lte rio ri fo n d i d o p o l’apertura. In fin e, o c c o r r e p rogettare un m e to d o withdraw c h e preleva
l ’in tero am m o n ta re d el saldo: n o n s o n o c o n se n titi p relievi parziali.

R 3 . 1 5 . A n alizzate qu esta classe, c h e v u o le rappresentare u n m o d e llo di fo r m e quadrate:

public class Square


{
private int sideLength;
private int area; // non è una buona idea

public Square(int length)


{
sideLength = length;
}
public int getAreaO
{
area = sideLength * sideLength;
return area;
}
}
P erch é n o n è una b u o n a idea usare un a variab ile di esem p lare p er m e m o r izz a re l ’area d el quadrato?
M o d ific a te la classe in m o d o c h e area sia una variab ile locale.

R 3 .1 6 . A n a lizza te qu esta classe, c h e v u o le rappresentare u n m o d e llo di fo r m e quadrate:

public class Square


{
private int sideLength;
private int area;

public Square(int initialLength)


{
sideLength = initialLength;
area = sideLength * sideLength;
}
public int getAreaO { return area; }
public void grow() { sideLength = 2 * sideLength; }
}
C h e errore c ’è in qu esta classe? C o m e lo correggereste?

R 3 .1 7 ( c o l l a u d o ) . P rogettate un a classe per il c o lla u d o di u n ità della classe Counter vista n el


Paragrafo 3 . 1 .

R 3 .1 8 ( c o l l a u d o ) . L e g g ete l ’ese rc iz io E 3 .1 2 , sen za im p lem en ta re, p er il m o m e n to , la classe Car.


S criv ete un a classe di c o lla u d o c h e sim u li u n o sc e n a r io di q u esto tipo: si a g g iu n g e carburante
all’a u to m o b ile , si gu id a u n p o ’, si a g g iu n g e di n u o v o u n p o ’ di carburante e, in fin e , si gu id a di
n u o v o .V isu a lizza te la qu antità di carburante p resen te n el serb atoio, oltre al su o valore previsto.

R 3 .1 9 . U s a n d o la te cn ic a di analisi d escritta n el Paragrafo 3 .5 , stu diate il p rogram m a p resen tato


alla fin e del Paragrafo 3 .4 .

R 3 .2 0 . U s a n d o la te cn ic a di analisi d escritta n el Paragrafo 3 .5 , stu diate il p rogram m a presen tato


nella se z io n e C o n sig li pratici 3 .1 .
Realizzare classi 135

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%

Progettate anche la classe EmployeeTester che collaudi tutti i metodi.

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:

public void addLine(String line)

Progettate un altro m etodo che restituisca l’intero testo della lettera:

public String getText()

11 testo della lettera ha il seguente formato:

Dear destinatario:
riga vuota
prima riga del contenuto della lettera
seconda riga del contenuto della lettera
138 C apitolo 3

ultima riga del contenuto della lettera


riga vuota
Sincerely,
riga vuota
mittente
P rogettate a n c h e il p rogram m a di c o lla u d o LetterPrinter c h e visu alizzi questa lettera:

Dear Dohn :

I am sorry we must part.


I wish you all the best.

Sincerely,

Mary
In esso, costruite un esemplare della classe Letter e invocate due volte il m eto d o addLine.

Suggerimenti: (1) U sa te il m e to d o concat p er costru ire una stringa p iù lu n ga a partire da d u e strin gh e


p iù co rte. (2) La stringa sp ecia le "\n" rappresenta il carattere sp ecia le (ch ia m a to neudine) c h e si usa
p er andare a capo. A d e se m p io , il se g u e n te e n u n cia to

body = body.concat("Sincerely,").concat("\n");

aggiunge al con ten u to della lettera la riga "Sincerely,".

E 3 .1 5 . R e a liz za te la classe Bug c h e rappresenti u n in se tto c h e si sposta lu n g o un a lin ea o r iz zo n ta le ,


verso sinistra o v erso destra. In iz ia lm e n te si sposta v erso destra, m a p u ò cam biare d irezio n e; o g n i
volta c h e si sposta, la sua p o s iz io n e lu n g o la lin ea cam b ia di u n ’un ità verso la d ir e z io n e p iù recen te.
D o ta te la classe di u n costru ttore:

public Bug(int initialPosition) // riceve la posizione iniziale

e dei m e t o d i :

public void turn() // cambia direzione


public void move() // si sposta di un'unità nella direzione attuale
public int getPositionO // ispeziona la posizione

E c c o u n e se m p io di u tilizzo:

Bug bugsy = new Bug(lO);


bugsy.moveO; // ora si trova nella posizione 11
bugsy.turn(); // cambia direzione
bugsy.moveO; // ora si trova nella posizione 10

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:

public Moth(double initialPosition) // riceve la posizione iniziale

e dei m e t o d i :

public void moveToLight(double lightPosition) // va verso la luce indicata


public double getPosition() // ispeziona la posizione
Realizzare classi 139

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.

E 3 .1 8 ( g r a f i c a ) . D ise g n a te u n b ersaglio (“ b u ll’s e y e ”), vale a dire una serie di cerch i c o n c e n tr ic i,


a ltern an d o i c o lo r i b ia n co e nero. 11 p rogram m a d ev e essere c o stitu ito dalle classi Target {b e rsa g lio ),
TargetComponent e TargetViewer. S u g g e r im e n to : C o lo r a te u n c e r c h io n ero nella sua intera su p erficie,
q u in d i so v ra p p o n ete u n c er ch io b ia n c o p iù p ic c o lo , e co sì via.

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.

E 3 .2 0 ( g r a f i c a ) . M ig lio ra te l ’ese rc iz io p r e c e d e n te d o ta n d o la classe House di un c o stru tto re c h e


co n sen ta di sp ecificare p o s iz io n e e d im e n sio n e della casa da visualizzare, q u in d i p o p o la te lo sc h e r m o
c o n u n p o ’ di case di varie d im e n sio n i.

E 3 .2 1 ( g r a f i c a ) . M o d ific a te il p rogram m a c h e d isegn a a u to m o b ili v isto n el Paragrafo 3 .8 in m o d o


c h e le a u to m o b ili v en g a n o visu alizzate in c o lo r i diversi. O g n i o g g e tto di tip o Car d eve m e m o rizza re
il p ro p rio c o lo r e. S criv ete le classi Car e CarComponent m o d ifica te.

E 3 .2 2 ( g r a f i c a ) . M o d ific a te la classe Car in m o d o c h e la d im e n sio n e di u n ’a u to m o b ile possa essere


sp ecificata n el co stru ttore. M o d ific a te la classe CarComponent in m o d o c h e una d e lle a u to m o b ili abbia
d im e n sio n i d o p p ie risp etto alla v e r sio n e o rig in a le .

E 3 .2 3 ( g r a f i c a ) . S criv ete un p rogram m a c h e d ise g n i la stringa “ H E L L O ” u tiliz z a n d o so lo lin e e e


cerch i. N o n in v o ca te il m e to d o drawString e n o n usate System.out. P rogettate le classi LetterH, LetterE,
LetterL e LetterO.

E 3 .2 4 ( g r a f i c a ) . S criv ete un p rogram m a p er visualizzare gli an elli o lim p ic i. C o lo r a te gli anelli


c o n i c o lo r i corretti (blu, nero, rosso, g ia llo e verd e, p r o c e d e n d o da sinistra a destra e dall’alto in
basso), p ro g etta n d o le classi OlympicRing, OlympicRingViewer e OlympicRingComponent.
140 C apitolo 3

E 3 .2 5 ( g r a f i c a ) . C o stru ite un g ra fico a barre p er rappresentare i dati seg u en ti:

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

D is p o n e te le barre o r iz z o n ta lm e n te , p er facilitare l ’in se r im e n to d e lle e tic h e tte in co rr isp o n d e n z a


di ciascu n a barra, e p rogettate le classi BarChartViewer e BarChartComponent,

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

Esercizi di riepiloi|® iJB jK9t(iju|jnfii)tp


* R 4 . 1 . S criv ete in Java en u n cia ti c h e d ic h ia rin o variabili adatte a m e m o r izz a re i dati d escritti
nel se g u ito , s c e g lie n d o in m o d o ap p rop riato tra n u m er i in teri e n u m er i in virgola m o b ile ed
e v e n tu a lm e n te d ich iaran d o costan ti q u a n d o tale scelta sia riten u ta adeguata.

a. Il n u m ero di g io r n i in una settim an a


b. Il n u m ero di g io r n i c h e m a n ca n o alla fin e del sem estre
c. Il n u m ero di c en tim e tri in u n p o llic e
d. L’altezza in c en tim e tri della person a p iù alta della classe

* R 4 . 2 . C h e valore ha mystery d o p o l ’e s e c u z io n e di questa seq u en za di en u n ciati?

int mystery = 1;
mystery = 1 - 2 * mystery;
mystery = mystery + 1;

* R 4 . 3 . C h e cosa c ’è di sb agliato in qu esta seq u en za di en u n ciati?

int mystery = l;
mystery = mystery + 1;
int mystery = 1 - 2 * mystery;

R 4 . 4 . S criv ete in n o ta z io n e m a tem atica le se g u e n ti esp ression i Java.

a. dm = m * (Math.sqrt(l + v / c) / Math.sqrt(l - v / c) - l);


b. volume = Math.PI * r * r * h;
c. volume = 4 * Math.PI * Math.pow(r, 3) / 3;
d. Z = Math.sqrt(x * x + y * y);

★★ R 4 . 5 . S criv ete in Java le se g u e n ti esp ression i m a tem a tich e.

^ 2
5 = 5q +V^)t+-gt

G = 4n^
{m^ + m2)
\ INT
FV = PV- 1 + ------
100.

C = —2aècosy
186 C apitolo 4

** R 4 . 6 . Ip o tiz z a n d o c h e a e b sian o variab ili di tip o in t , c o m p le ta te la tabella.

Math.pow(a^ b) Math.max(a, b) a / b a %b Math.floorMod(a, b)


2 3
3 2
2 -3
3 -2
-3
-3

R 4 .7 . Im m a g in a te c h e direction sia la m isura di u n a n g o lo , espressa c o m e n u m er o in tero c o m p r eso


tra 0 e 3 5 9 gradi. D o p o aver c o m p iu to una ro ta zio n e di turn gradi, la variab ile direction v ie n e
aggiorn ata in q u esto m o d o :

direction = (direction + turn) %360;

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))

R 4 . 9 . Ip o tiz z a n d o c h e n abbia il valore 17 e c h e m abbia il valore 18, quali so n o i valori d elle


se g u e n ti espressioni?

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

R 4 . 1 1 . In d ivid u ate a lm e n o c in q u e errori d i c o m p ila z io n e n el p rogram m a se g u e n te .

public class HasErrors


{
public static void main();
T ipi di dati fondamentali 187

System.out.print(Please enter two numbers:)


X = in.readDouble;
y = in.readDouble;
System.out.printline("The sum is " + x + y);

}
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

(d im e ) e da c in q u e c e n te sim i (n ic k e l) , ip o tiz z a n d o c h e il p rezzo sia un m u ltip lo di 5 c e n te sim i. Per


svilup pare lo p s e u d o c o d ic e fate p rim a u n p aio di e sem p i ela b o ra n d o valori sp ecifici.

★ ★ ★ R 4 .2 0 . Per utilizzare il p rogram m a svilu p p ato n ella se z io n e E sem p i c o m p le ti 4 .1 , è abbastanza facile


m isurare la lu n g h e zz a d el lato di base di un a p iram id e. Per m isu rarn e l ’altezza sen za arram picarsi
fin o in cim a si p u ò usare un te o d o lite e d eterm in are l’a n g o lo tra il te rr en o e la lin ea id eale c h e
c o n g iu n g e la p o s iz io n e d el te o d o lite e la cim a della p iram id e. Q u a li altre in fo r m a z io n i se r v o n o
p er calcolare l ’area della su p erficie della piram id e?

★ ★ ★ 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 .

** R 4 . 2 2 . U n o shaker p er co ck ta il si p u ò con sid erare c o stitu ito da tre tro n ch i di c o n o . U s a n d o


valori realistici per i raggi e le altezze, ca lco la te il v o lu m e to ta le d el c o n te n ito r e , u san d o la form u la
presentata nella d om a n d a di a u to -v a lu ta z io n e 21 p er u n sin g o lo tr o n c o di c o n o , p o i svilu p p ate un
a lg o r itm o c h e fu n z io n i c o n qualsiasi in sie m e di m isure.

R 4 . 2 3 . Si v u o l tagliare un a fetta di torta, c o m e in d ic a to in figura, d o v e r è la lu n g h e zz a del taglio


(te c n ic a m e n te , una cord a della circon feren za) e h è la “ larg h ezza ” della fetta. Per c a lco la rn e l’area
si p u ò usare la se g u e n te fo rm u la, approssim ata:

, 2 ^ /r'
A = -c h + —
3 2c

Va n o ta to , p e r ò , c h e h n o n è fa c ile da m isurare, m e n tre so lita m e n te il d ia m e tro d d ella torta è


n o to . C a lc o la te l ’area della fetta n el ca so in c u i il d ia m e tro sia 12 p o llic i e la cord a sia 10 p o llic i,
p o i g e n e ra lizz a te l ’a lg o r itm o in m o d o c h e c a lc o li l ’area p er qualsiasi valore d el d ia m e tro e della
cord a.
T ipi di dati fondamentali 189

Lo pseudocodice seguente descrive come calcolare il nome di un giorno in inglese, dato


R 4 .2 4 .
il suo indice (0 = Sunday, 1 = Monday, e così via).
Definire la stringa names = "SunMonTueWedThuFriSat"
Calcolare la posizione iniziale pos = 3 • indice del giorno
Estrarre da names la sottostringa di lunghezza 3 a partire da pos

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

Verificate la correttezza di questo pseudocodice usando la stringa "Gateway" e le posizioni 2 e 4.


Tracciate uno schema della stringa che viene calcolata, in analogia con la Figura 3.
Come si ottiene il primo carattere di una stringa? E Fultimo? Come si elimina il primo
R 4 .2 6 .
carattere da una stringa? E l’ultimo?
Per ciascuno dei seguenti calcoli eseguiti in Java, determinate se il risultato sarà esatto
R 4 .2 7 .
oppure se si verificherà un overflow o un errore di arrotondamento:
a. 2.0 - 1.1
b. 1.0E6 * 1.0E6
c. 65536 ♦ 65536
d. 1_000_000L * 1_000_000L

R 4 . 2 8 . Scrivete un programma che visualizzi il risultato delle espressioni seguenti e fornite


spiegazioni per quanto accade:
3 * 1000 * 1000 * 1000
3.0 * 1000 * 1000 * 1000

E4.1. Scrivete un programma che visualizzi le dimensioni in millimetri di un foglio di carta in


formato “letter” (8.5 • 11 pollici, un pollice = 25.4 millimetri). Usate le costanti opportune e
commentate adeguatamente il codice.
E4.2. Scrivete un programma che calcoli e visualizzi il perimetro e la lunghezza della diagonale
di un foglio di carta in formato “letter” (8.5 • 11 pollici).
E4.3. Scrivete un programma che legga un numero e ne visualizzi il quadrato, il cubo e la quarta
potenza. Usate il metodo Math.pow soltanto per calcolare la quarta potenza.
E4.4. Scrivete un programma che chieda all’utente di fornire due numeri interi e che poi ne
stampi:
190 C apitolo 4

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)

Su^erimento: nella classe Math sono presenti i metodi max e min.


E4.5. Migliorate la soluzione dell’esercizio precedente in modo che i numeri vengano visualizzati
con l’incolonnamento opportuno, come in questo esempio:
Sum: 45
Difference : -5
Product : 500
Average : 22.50
Distance: 5
Maximum: 25
Minimum: 20

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

(se usate u n sistem a U n i x o M a cin to sh , ig n o ra te la lettera c h e id en tifica il d isc o e usate / in v e ce


di \ p er separare tra lo ro i n o m i d e lle ca rtelle).

E 4 . 1 3 . S criv ete u n p r o g r a m m a c h e legga u n n u m er o c o m p r e so tra 1 0 0 0 e 9 9 9 9 9 9 , d o p o averlo


c h ie s to all’u te n te , il q u a le inserirà il n u m er o u san d o la virg o la p er separare le m igliaia, s e c o n d o
l ’uso an g lo sa sso n e (es. 1 0 ,0 0 0 o p p u r e 9 9 ,9 9 9 ). P o i, visu alizzate il n u m er o senza la virgola. Per
chiarire m e g lio il p r o b le m a , v e d ia m o un e se m p io di d ia lo g o tra il p rogram m a e l’u te n te (i dati
fo rn iti d all’u te n te s o n o r ip o rta ti in grassetto):

Please enter an integer between 1,000 and 999,999: 23,456


23456

S u ^ e r i m e n t o . A c q u i s i t e il d a to so tto form a di stringa e m isuratela. D ic ia m o c h e c o n te n g a n caratteri:


estraete le so tto str in g h e c o n t e n e n t i i p rim i n — 4 caratteri e gli u ltim i tre caratteri.

E 4 .1 4 . S criv ete un p ro g r a m m a c h e legga u n n u m er o c o m p r eso tra 1 0 0 0 e 9 9 9 9 9 9 , d o p o averlo


ch iesto all’uten te, per p o i v isu alizzarlo c o n una virgola c o m e separatore d elle m igliaia, se c o n d o l ’u so
an g lo sasson e. Per ch iarire m e g lio il p rob lem a, v e d ia m o u n e se m p io di d ia lo g o tra il p rogram m a e
l’u te n te (i dati forn iti d a ll’u te n te s o n o rip ortati in grassetto):

Please enter an integer between 1000 and 999999: 23456


23,456

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:

System, o u t. p rin tln ( "+—+—+—+" ) ;

m a cercate, in v e ce , di farlo in m o d o più e le g a n te e flessibile. D ich iarate variabili stringa c h e m e ­


m o r iz z in o d u e sc h e m i diversi: u n a sp ec ie di p e ttin e (c o m e q u e llo c o stitu ito dalle p r im e d u e rig h e
della scacch iera) e l ’u ltim a riga. V isu alizzate per tre v o lte lo sch em a a p e ttin e , c o n c lu d e n d o c o n
una v isu a lizz a z io n e d e ll’u ltim a riga.

E4.16. S criv ete un p rog ra m m a c h e legga u n n u m er o in tero di c in q u e cifre e lo sc o m p o n g a in una


seq u en za d e lle su e s in g o le cifre. A d e se m p io , r ic e v e n d o in in gresso il n u m er o 1 6 3 8 4 , il program m a
d ev e visualizzare:

1 6 3 8 4

P o tete ip o tizzare c h e il n u m e r o fo r n ito n o n sia n e g a tiv o e n o n abbia p iù di c in q u e cifre.

E4.17. S criv ete u n p rogram m a c h e legga d u e orari in fo r m a to m ilitare (ad e se m p io , 0 9 0 0 o 17 3 0 )


e visu alizzi il n u m er o di o re e di m in u ti c h e li separano n el te m p o , c o m e n el se g u e n te e se m p io di
e s e c u z io n e (c o n i dati in tr o d o tti d all’u te n te in grassetto):

Please enter the first time: 0900


Please enter the second time: 1730
8 hours 30 minutes
192 C apitolo 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:

* ♦
* ♦

e può essere ottenuta dichiarando una stringa letterale come questa:


final String LEnER_H = "* *\n* *\n*****\n*

(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):

/\
/ \
/ \
/ \

E4.21. Migliorate la classe CashRegister aggiungendo i metodi enterDollars,enterQuarters,enterDimes,


enterNickels e enterPennies. Usate questa classe di collaudo:

public class CashRegisterTester


{
public static void main(String[] args)
{
CashRegister register = new CashRegister();
register.recordPurchase(20.3 7 );
register.enterDollars(20);
register.enterOuarters(2);
System.out.println("Change: " + register.giveChange());
T ipi di dati fondamentali 193

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.

Con enunciati i f si può decidere se i dati acquisiti sono validi


• Per co n tr o lla re se il d ato su cc e ssiv o in in g r esso è u n n u m er o , si p u ò in v o c a re hasNextInt o
hasNextDouble.

Elementi di libreria presentati in questp c^jtplo


java.awt.Rectangle java.util.logging.Level
equals INFO
java.lang.String OFF
equals java.util.logging.Logger
COfnpareTo getClobal
java.util.Scanner info
hasNextDouble setLevel
hasNextInt

,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?

a. int n = l; int k = 2; int r = n;


if (k < n) { r = k; }
b. int n = 1; int k = 2; int r;
if (n < k) { r = k; }
else { r = k + n; }
c. int n = 1; int k = 2; int r = k;
if (r < k) { n =r; }
else { k = n; }
d. int n = 1; int k = 2; int r = 3;
if (r <n + k) { r= 2 * n; }
else { k = 2 * r; }

** R 5 .2 . S p ieg a te la differenza tra:

S = O
if (x > O) { S++; }
if (y > 0) { S++; }

S = O
if (x > O) { S + + ; }
else if (y > 0) { s++; }

** R 5 .3 . T rovate gli errori n ei se g u e n ti e n u n cia ti if :

a. if X > 0 then System.out.print(x);


b. if (l + X > Math.pow(x, Math.sqrt(2)) { y = y + x; }

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.4. Cosa visualizzano questi frammenti di codice?


a. int n a 1;
int m = -1;
if (n < -m) { System.out.print(n); }
else { System.out.print(m); }
b. int n *1;
int m a -1;
if (-n >= m) { System.out.print(n); }
else { System.out.print(m); }
c. double X a 0;
double y = 1;
if (Math.abs(x - y) < l) { System.out.print(x); }
else { System.out.print(y); }
d. double x = Math.sqrt(2);
double y = 2;
if (x * X == y) { System.out.print(x); }
else { System.out.print(y); }

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;

Cosa fa il compilatore quando si compila il programma?

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

Illustrate le differenze esistenti tra enunciati


R 5 .2 2 . if annidati e una sequenza if /e ls e if /e ls e ,
fornendo esempi di entrambi i casi.
R 5 .2 3 . Fornite un esempio di una sequenza i f / e l s e i f / e l s e in cui l’ordine delle verifiche è
ininfluente, e un altro esempio in cui, invece, l’ordine è influente.
R5.24. Riscrivete la condizione vista nel Paragrafo 5.3 in modo che usi operatori < al posto di
operatori >=. Come va modificato l’ordine dei confronti?
Progettate un insieme di casi di prova per il programma del calcolo delle tasse
R 5 .2 5 ( c o lla u d o ) .
presentato nell’Eserdzio P5.2 e calcolate a mano i valori previsti.
R5.26. Scrivete un frammento di codice Java che illustri il problema dell’else sospeso usando
questa frase: uno studente con voto complessivo (nel sistema anglosassone GVA,j^rade point average)
inferiore a 2 ma non inferiore a 1.5 è “rimandato”, uno con GPA inferiore a 1.5 è bocciato.
R5.27. Completate la tabella della verità seguente, calcolando i valori assunti dalle espressioni
booleane indicate per tutte le combinazioni dei valori di ingresso p, q e r.

P q r (P &&q) II !r !(P &S (q II !r))


falso falso falso
falso falso vero
falso vero falso

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.

R 5 .3 0 . Se il valore di b è fa lse e il valore di x è 0, qual è il valore di ciascuna delle seguenti espres-


sioni?

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 1 . Semplificate le espressioni seguenti, dove b è una variabile di tipo boolean.


a. b == true
b. b == fa lse
c. b != true
d. b != fa lse
D ecisioni 251

R 5 .3 2 . Semplificate gli enunciati seguenti, dove b è una variabile di tipo boolean e n è una variabile
di tipo int.

a. if (n == 0) { b = true; } else { b = false; }

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; }

R 5 .3 3 . Cosa c’è di sbagliato nel programma seguente?


System.out.print("Enter the number of quarters: ");
int quarters = in.nextlnt();
if (in.hasNextIntO)
{
total = total + quarters * 0.25;
System.out.println("Total: " + total);
}
else
{
System.out.println("Input error.")
}

«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.

Progettate la classe Grade dotata del metodo getNumericCrade.


ES. 17. Scrivete un programma che traduca un numero compreso tra 0 e 4 nel voto letterale più
vicino, usando le regole definite nell’esercizio precedente. Ad esempio, il numero 2.8 (che potrebbe
essere la media di più voti) deve essere convertito in B-. Risolvete i casi di parità in favore del
voto migliore: ad esempio, 2.85 deve essere convertito in B. Progettate la classe Grade dotata del
metodo getLetterGrade.
ES. 18. Nel 1913 lo schema di tassazione statunitense era abbastanza semplice. L’importo da pagare
era:
1% sulla quota di reddito inferiore a $50000
2% sulla quota di reddito compresa tra $50000 e $75000
3% sulla quota di reddito compresa tra $75000 e $100000
4% sulla quota di reddito compresa tra $100000 e $250000
5% sulla quota di reddito compresa tra $250000 e $500000
6% sulla quota di reddito superiore a $500000
254 C apitolo 5

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)

Poi, il programma deve visualizzare la descrizione completa della carta. Ad esempio:


Enter the card notation: QS
Queen of Spades

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

P ro g ettate la classe Month dotata d el m e to d o :

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.

Denaro speso Percentuale del buono


Meno di $10 Nessun buono
Da $10 a $60 8%
Da più di $60 a $150 10%
Da più di $150 a $210 12%
Più di $210 14%

Ecco un esempio di esecuzione del programma:


Please enter the cost of your groceries: 14
You win a discount coupon of $1.12 (8% of your purchase).

Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
Iterazioni 327

Con un debugger sì individuano errori durante Tesecuzione


• U n d e b u ^ e r è un p rogram m a c h e si p u ò usare p er eseg u ire u n altro p rogram m a e analizzare
il su o c o m p o r ta m e n to du ran te l ’e s e c u z io n e .
• P o tete usare e ffic a c e m e n te il d e b u g g er c o m p r e n d e n d o a fo n d o tre c o n c etti: p u n ti di arresto
( b r e a k p o in t), e s e c u z io n e passo d o p o passo (sinj^le s te p ) e is p e z io n e di variabili.
• Q u a n d o il d e b u g g e r e s e g u e u n p r o g r a m m a , l’e s e c u z io n e v i e n e in te r r o tta o g n i v o lta c h e
v i e n e r a g g iu n to u n p u n t o di arresto.
• 11 c o m a n d o sinj^le s te p e se g u e il p rogram m a una riga alla volta.
• Per id en tificare il p u n to in cu i il program m a fallisce, usate la tecn ica del “ divid ere p er v in c e r e ’’.
• D u ra n te il d e b u g g in g co n fro n ta te il c o n te n u to d elle variabili c o n i valori c h e avevate previsto.

Elementi di libreria presentati in questo capitolo


java.util.Random
nextDouble
nextint

J^serdzj di riepilogo e approfondimento


* R 6 . 1 . D a te le variabili

String stars = "*****";


String stripes = "=====";

cosa visu a lizza n o i cicli segu en ti?


a. in t i = 0;
while ( i < 5)
{
System.o u t. p r in tln (sta r s. su bstring(0, i ) );
i++;
}
b. int i = 0;
while (i < 5)
{
System.out.print(stars.substring(0, i));
System.out.println(stripes.substring(i, 5));
i-H-;
}
c. int i = 0;
while (i < io )
{
i f ( i % 2 == 0) { System.o u t.p r in tln (sta r s); }
e ls e { S ystem .ou t.p rin tln (s tr ip e s ); }
}

R 6 . 2 . C o sa visu a lizza n o i cicli segu en ti?

a. int i = 0; int j = 10;


while (i < j) { System.out.println(i + " " + j); i++; j— ; }
b. int i = 0; int j = 10;
while (i < j) { System.out.println(i + j); i++; j++; }
328 C apitolo 6

* R6.3. Cosa visualizzano i cicli seguenti?


a. int result = 0;
for (int i = 1; i <= 10; i++) { result = result + i; }
System.out.println(result);
b. int result = 1;
for (int i = l; i <= 10; ì -h -) { result = i - result; }
System.out.println(result);
c. int result = l;
for (int i = 5; i > 0; i--) { result = result * i; }
System.out.println(result);
d. int result = l;
for (int i = 1; i <= 10; i = i * 2) { result = result * i; }
System.out.println(result);

R6.4. Scrivete un ciclo while che visualizzi:


a. tutti i numeri minori di n che sono quadrati perfetti (ad esempio, se n vale 100, visualizza 0
1 4 9 16 25 36 49 64 81);
b. tutti i numeri positivi minori di n che sono divisibili per 10 (ad esempio, se n vale 100,
visualizza 10 20 30 40 50 60 70 80 90);
c. tutti i numeri minori di n che sono potenze di 2 (ad esempio, se n vale 100, visualizza 1 2
4 8 16 32 64).

R6.5. Scrivete un ciclo che calcoli:


a. la somma di tutti i numeri pari compresi tra 2 e 100 (estremi inclusi);
b. la somma di tutti inumeri compresi tra 1 e 100(estremi inclusi) che sono quadrati perfetti;
c. la sommadi tutti i numeri dispari compresi tra a e b (estremiinclusi);
d. la somma di tutte le cifre dispari del numero n(ad esempio,se n vale 32677, la somma
descritta è3 + 7 + 7 = 17).

^ 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.7. Cosa visualizzano i cicli seguenti?


a. for (int i= 1; i< 10; i++) { System.out.print(i + " "); }
b. for (int i= 0; i< 10; i +=2) { System.out.print(i + " "); }
c. for (int i= 10; i > 1; i— ) { System.out.print(i + " ”); }
d. for (int i= 0; i< 10; i++) { System.out.print(i + " "); }
e. for (int i = 1; i < 10; i = i * 2) { System.out.print(i + ” "); }
f for (int i = I; i < 10; i++) { if (i % 2 == 0) { System.out.print(i + " " ) ; } }

^ R6.8. Cos’è un ciclo infinito? Nel vostro computer, in che modo potete terminare un programma
che sta eseguendo un ciclo infinito?
Iterazioni 329

R 6 .9 . Nell’ipotesi che i valori in ingresso siano 4 7 -2 -5 0, eseguite a mano lo pseudocodice


dell’Esercizio E6.7, tenendone traccia su carta.
R 6 .1 0 . Cos’è un errore “per scarto di uno’’? Fornite un esempio tratto dalla vostra esperienza di
programmazione.
R 6 .1 1 . Cos’è un valore sentinella? Fornite una regola semplice che dica quando !’utilizzo di un
valore sentinella di tipo numerico è appropriato.
Quali enunciati di ciclo esistono in Java? Fornite regole semplici che dicano quando è più
R 6 .1 2 .
opportuno usare ciascun tipo di ciclo.
Quante iterazioni eseguono i cicli
R 6 .1 3 .
corpo del ciclo?
a. for (int i = I; i <= 10; i++) . . ,
b. for (int i = o; i < 10; i++) . . .
c. for (int i = io; i > o; i— ) . . .
d. for (int i = -io; i <= 10; i++) . ,. .
e. for (int i = io; i >= 0; i++) . .
f. for (int i = -io ; i <= 10; i = i + 2) .
g- for (int i = -io ; i <= 10; i = i + 3) .

R 6 .1 4 . Scrivete lo pseudocodice per un programma che visualizzi un calendario come questo:


Su M T W Th F Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31*

* 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.

R6.18. Riscrivete il seguente ciclo for sotto forma di ciclo while,


int S = O ;
for (int i = 1; i <= 10; i++)
{
S = S + i;
}

R6.19. Riscrivete il seguente ciclo do sotto forma di ciclo while.


int n = in.nextIntO;
double X = O;
double s;
do
{
S = 1.0 / (l + n * n);
n++;
X = X + s;
}
while (s > O.Ol);

R 6 .2 0 . Tenendone traccia su carta, eseguite a mano i cicli seguenti:


a. int S= 1;
int n = 1;
while (s < io) { S = S + n; }
n-H-;
b. int S = 1;
for (int n = 1; n < 5; n++) { s = s + n; }
c. int S= 1;
int n = 1;
do
{
S = S + n;
n++;
}
while (s < 10 * n);

Cosa visualizzano i cicli seguenti?Trovate la risposta senza usare il computer, eseguendoli


R 6 .2 1 .
a mano e tenendo traccia dell’esecuzione su carta.
a. int S= 1;
for (int n = 1; n <= 5; n++)
{
S = S + n;
Systen.out.print(S + " ”);
}
b. int S= 1;
for (in t n = 1; S <= 10; System.out.print(s + " ''))
{
n = n + 2;
Iterazioni 331

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:

Scrivete un unico ciclo che visualizzi lo stesso rettangolo.


R6.31. Immaginate di dove progettare un gioco educativo che insegni ai bambini a leggere
l’orologio. Come pensate di generare valori casuali per le ore e i minuti?
R6.32. Nella simulazione di un viaggio, Harry farà visita a uno dei suoi amici, che abitano in tre
stati diversi: ha dieci amici in California, tre in Nevada e due nello Utah. Come pensate di generare
un numero casuale, compreso tra 1 e 3, che indichi lo stato di destinazione, con una probabilità
proporzionale al numero di amici di Harry che abitano in quello stato?
R6.33 (collaudo). Spiegate le differenze fra queste operazioni di debugging:
• Procedere con l’esecuzione all’interno di un metodo (“step into”)
• Procedere con l’esecuzione senza entrare in un metodo (“step over”)

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

public void add(double value)


334 C apitolo 6

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.

Traducete in un programma Java la seguente descrizione in pseudocodice, che permuta in


E 6 .8 .
modo casuale i caratteri di una stringa.
Leggi una parola e memorizzala in word.
Ripeti un numero di volte pari a word.lengthO
Scegli a caso una posizione i in word, che non sia l'ultima posizione.
Scegli a caso una posizione j in word, con j > i.
Scambia le lettere che si trovano nelle posizioni i e j.
Visualizza word.

Per scambiare le lettere, costruite le tre sottostringhe (first, middle e la st) indicate nella figura.

fir s t middle D la st

Quindi, sostituite la stringa originaria con:


first + word.charAt(j) + middle + word.charAt(i) + 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.13. Scrivete un programma che visualizzi tutte le potenze di 2, da 2‘*a 2^‘*,


E6.14. Scrivete un programma che legga un numero intero (number) e visualizzi le cifre della sua
rappresentazione binaria, in ordine inverso: visualizzate per prima cosa il resto della divisione per
due, number %2, poi sostituite il numero con number / 2; continuate fino a quando il numero diventa
0. Se, ad esempio, l’utente digita il numero 13, il programma deve visualizzare:
1
0
1
1

^ 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

Per gestire sequenze di dimensione variabile si usano vettori


• U n v etto r e m e m o r iz z a una se q u en za di valori e la lu n g h e zza della se q u en za è variabile.
• La classe A rra y L ist è u n a classe g e n e r ic a : ArrayList</Vo/neTi/?o> c o n t ie n e e le m e n ti di tip o
NomeTipo.
• Per c o n o sc e r e la d im e n s io n e di u n v etto r e si usa il m e to d o s i z e .
• Per a cced er e a u n e le m e n to di un v etto r e u san d o il su o in d ic e si in v o c a n o i m e to d i g e t e s e t.
• Per a g g iu n g ere o rim u o v ere e le m e n ti in u n v etto r e si in v o c a n o i m e to d i add e remove.
• Per in serire valori di u n tip o p r im itiv o in u n v etto r e si d e v o n o usare le classi in volu cro.

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.

OSiUSDlUi libreria presentati in questo capitolo /'4ÌV.v< tfrr

java.lang.Boolean java.util.ArrayList<E>
java.lang.Double add
java.lang.Integer get
java.util.ArrayS remove
copyOf set
toString size

Esercizi di riepilogo «jaaurfUllSliniSJllP


R 7 . 1 . U sa n d o un u n ic o array, riso lv e te i se g u e n ti p r o b le m i,s e g u e n d o l ’o rd in e in cu i so n o proposti:

a. D ich iarate un array p red isp osto p er c o n te n e r e d ieci n u m eri in teri.


b. In serite il n u m ero 17 c o m e e le m e n to in iziale d e ll’array.
c. In serite il n u m er o 2 9 c o m e e le m e n to finale d e ll’array.
d. A ssegn ate - 1 a tutti gli altri e le m e n ti.
e. A g g iu n g e te 1 a tutti gli e le m e n ti d e ll’array.
f V isu alizzate tutti gli e le m e n ti d e ll’array, u n o per riga.
g. V isu alizzate tutti gli e le m e n ti d e ll’array in u n ’un ica riga, separati da virg o le.

R 7 .2 . In u n array, c o s ’è u n in d ice? Q u a li so n o i valori validi p er u n in d ic e in u n array? C o s ’è un


errore di lim iti?

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?

R 7 .4 . S criv ete un c ic lo c h e le g g e d ie c i n u m e r i e u n se c o n d o c ic lo c h e li visu alizza in o rd in e


in verso risp etto a c o m e so n o stati acq u isiti.

R 7 .5 . S criv ete u n fra m m en to di c o d ic e c h e riem p ia !’array v a lu e s c o n cia sc u n o d ei se g u e n ti


in sie m i di n u m eri:

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.6. Dato il seguente array:


int[] a = { 1, 2, 3, 4, 5, 4, 3, 2, 1 , 0 };

qual è il valore di total al termine dell’esecuzione di ciascuno dei cicli seguenti?


a. int total = 0;
for (int i = 0; i < 10; i++) { total = total + a[i]; }
b. int total = 0;
for (int i = 0; i < 10; i = i + 2) { total * total + a[i]; }
c. int total = 0;
for (int i = 1; i < 10; i = i + 2) { total = total + a[i]; }
d. int total = 0;
for (int i = 2; i <= 10; i++) { total = total + a[i]; }
e. int total = 0;
for (int i = 1; i < 10; i = 2 * i) { total = total + a[i]; }
f. int total = 0;
for (int i = 9; i >= 0; i— ) { total = total + a[i]; }
g. int total = 0;
for (int i = 9; i >= 0; i = i - 2) { total s total + a[i]; }
h. int total = 0;
for (int i = 0; i < 10; i++) { total = total - a[i]; }

R7.7. Dato il seguente array:


int[] a = { 1, 2, 3, 4, 5, 4, 3, 2, 1 , 0 };

qual è il suo contenuto al termine dell’esecuzione di ciascuno dei cicli seguenti?


a. for (int i = 1; i < 10; i++) { a[i] = a[i - l]; }
b. for (int i = 9; i > 0; i— ) { a[i] = a[i - l]; }
c. for (int i = 0; i < 9; i++) { a[i] = a[i + l]; }
d. for (int i = 8; i >= 0; i--) { a[i] = a[i + l]; }
e. for (int i = l; i < 10; i++) { a[i] = a[i] + a[i - l]; }
f. for (int i = 1; i < 10; i = i + 2) { a[i] = 0; }
g. for (int i = 0; i < 5; i++) {a[i + 5] = a[i]; }
h. for (int i = i; i < 5; i++) { a[i] = a[9 - i]; }

★★★ 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[].

a. for (double x : values) { total = total + x; }


b. for (double x : values) { if (x ==target) { return true; } }
c. int i = 0;
for (double x : values) { values[i] = 2 * x; i++; }

R7.13. Riscrivete i cicli seguenti usando il for esteso, tenendo presente che values è di tipo dou-
ble[].

a. for (int i = 0; i < values.length; i++) { total = total + values[i]; }


b. for (int i = 1; i < values.length; i++) { total = total + values[i]; }
c. for (int i = 0; i < values.length; i++)
{
if (values[i] == target) { return i; }
}

★ R7.14. Cosa c’è di sbagliato nei seguenti frammenti di codice?


a. ArrayList<int> values = new ArrayList<int>();
b. ArrayList<Integer> values = new ArrayList();
c. ArrayList<Integer> values = new ArrayList<Integer>;
d. ArrayList<Integer> values = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++)
{
values.set(i - l, i ♦ i);
}
e. ArrayList<Integer> values;
for (int i = 1; i <= 10; i++)
{
values.add(i * i);
}
R7.15. Per le operazioni seguenti, che agiscono su array riempiti solo in parte, scrivete le intestazioni
dei metodi, senza implementarli:
A rray e vettori 405

a. D isp o rre gli e le m e n ti in ord in e d ecrescen te.


b. V isualizzare tutti gli e le m e n ti, separati da una stringa data.
c. C o n ta re quanti e le m e n ti so n o m in o r i di un valore dato.
d. E lim inare tutti gli e le m e n ti c h e so n o m in o r i di un valore dato.
e. C op ia re in un altro array tutti gli e le m e n ti c h e so n o m in o r i di u n valore dato.

R 7 . 1 6 . E seg u ite a m an o, passo d o p o passo, il c ic lo v isto n el Paragrafo 7 .3 .4 , u san d o i dati là in d icati.


T e n e te traccia su carta d e ll’e se c u z io n e , m o stra n d o in d u e c o lo n n e il valore di i e q u a n to v ie n e
v isu alizzato.

R 7 . 1 7 . C o n sid e ra te il c ic lo se g u e n te , c h e ra cco g lie tutti gli e le m e n ti c h e so d d isfa n o una d e te r ­


m in ata c o n d iz io n e : in q u esto caso, c h e l’e le m e n to sia m a g g io re di 100.

ArrayList<Double> matches = new ArrayList<Double>();


for (double element : values)
{
if (element > IOO)
{
matches.add(element);
}
}
S a p en d o c h e v a lu e s c o n tie n e gli e le m e n ti 1 10, 9 0 , 1 0 0 , 1 2 0 e 8 0 , e se g u ite il c ic lo a m an o, passo
d o p o passo, te n e n d o traccia su carta d e ll’e se c u z io n e , m o stra n d o in d u e c o lo n n e il valore di elem en t
e il c o n te n u to di m atches.

R 7 . 1 8 . E se g u ite a m an o, passo d o p o passo, il c ic lo v isto n el Paragrafo 7 .3 .5 , n e ll’ip o tesi c h e v a lu e s


c o n te n g a gli e le m e n ti 8 0 , 9 0 , 1 0 0 , 1 2 0 e 1 lO .T en ete traccia su carta d e ll’e se c u z io n e , m o stra n d o in
d u e c o lo n n e il valore di pos e il valore di found. R ip e te te l’ese rc iz io n el caso in cu i v a lu e s c o n te n g a
gli e le m e n ti 8 0 , 9 0 , 1 2 0 e 7 0 .

R 7 . 1 9 . E seg u ite a m an o, passo d o p o passo, l ’a lg o r itm o di e lim in a z io n e di un e le m e n to v isto n el


Paragrafo 7 .3 .6 , n e ll’ip o tesi c h e v a lu e s c o n te n g a gli e le m e n ti 1 1 0 , 9 0 , 100, 1 2 0 e 8 0 , e lim in a n d o
l ’e le m e n to c o r r is p o n d e n te all’in d ic e 2.

R 7 . 2 0 . P rogettate m e d ia n te p s e u d o c o d ic e u n a lg o r itm o c h e ru oti di una p o s iz io n e gli e le m e n ti


di un array, sp o sta n d o alla fin e d e ll’array il su o e le m e n to in iziale, c o m e nella figura.

3| 5| 7 | u | l 3 l
/ / / / /
3| 5l 7 | n | l 3 l T |

R 7 . 2 1 . P rogettate m e d ia n te p s e u d o c o d ic e un a lg o r itm o c h e e lim in i da un array tutti i valori


n e g a tiv i, preservan d o l ’o rd in e relativo tra gli e le m e n ti rim a n en ti.

R 7 . 2 2 . N e l l ’ip o te si c h e v a lu e s sia un array o r d in a to di n u m er i in te ri, p rogettate m e d ia n te p seu ­


d o c o d ic e un a lg o r itm o c h e vi in serisca u n n u o v o e le m e n to in m o d o c h e, al te rm in e , !’array sia
ancora ord in ato.

R 7 . 2 3 . In un array, c h ia m ia m o “ se r ie ” (r u n ) una seq u en za di valori ad iacen ti rip etu ti. P rogettate


m e d ia n te p s e u d o c o d ic e u n a lg o r itm o c h e ca lco li la lu n g h e zz a della p iù lu n ga serie p resen te in un
array. A d e se m p io , in q u esto array la serie p iù lu n ga ha lu n g h e zz a 4:

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];

Assegnare 0 a tutti gli elementi.


Assegnare alternativamente i valori 0 e 1 agli elementi, in modo da creare una scacchiera.
Assegnare 0 soltanto agli elementi che si trovano nella prima e neH’ultima riga.
Calcolare la somma di tutti gli elementi.
Visualizzare tutti gli elementi in formato tabulare.
A rray e vettori 407

** 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.33. Come si risolvono i problemi seguenti?


a. Verificare se due array contengono gli stessi elementi, nello stesso ordine.
b. Copiare il contenuto di un array in un altro.
c. Riempire di zeri un array, sovrascrivendo tutti i suoi elementi.
d. Eliminare da un array tutti i suoi elementi.
^ R7.34. Vero O falso?
a. Tutti gli elementi di un vettore sono dello stesso tipo.
b. Gli indici usati in un vettore devono essere numeri interi.
c. I vettori non possono avere stringhe come elementi.
d. I vettori possono modificare la propria dimensione, aumentandola o diminuendola.
e. Un metodo non può restituire un vettore.
f Un metodo non può modificare la lunghezza di un vettore 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

allora il contenuto dell’array deve diventare il seguente:

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

public void add(double value)

che aggiunge un valore alla sequenza, se c’è ancora posto.


Aggiungete altri metodi per calcolare la somma, il valor medio, il valore massimo e il valore minimo
della sequenza.
E7.10. Scrivete metodi che risolvano i problemi seguenti per un array di numeri interi, fornendo
un programma di collaudo per ciascun metodo. 1 metodi vanno inseriti a completamento della
classe seguente:
A rray e vettori 409

public class ArrayMethods


{
private int[] values;
public ArrayMethods(int[] initialValues) { values = initialValues; }
public void swapFirstAndLastO { . . . } / / risolve il problema a.
public void shiftRightO { . . . } / / risolve il problema b.

} * *

a. Scambiare tra loro il primo e !’ultimo elemento dell’array.


b. Far scorrere tutti gli elementi di una posizione “verso destra”, spostando l’ultimo elemento
nella prima posizione. Ad esempio, !’array 1 4 9 16 25 deve diventare 25 1 4 9 16.
c. Sostituire con 0 tutti gli elementi pari.
d. Sostituire ciascun elemento, tranne il primo e l’ultimo, con il più grande dei due elementi
adiacenti.
e. Eliminare l’elemento centrale dell’array se questo ha dimensione dispari, altrimenti eliminare
i due elementi centrali.
f. Spostare tutti gli elementi pari all’inizio dell’array, preservando però l’ordinamento relativo tra
gli elementi, se si esclude la condizione imposta.
g. Restituire il secondo valore maggiore dell’array, cioè il valore massimo tra quelli inferiori al
valore massimo presente nell’array.
h. Restituire true se e solo se !’array è ordinato in senso crescente.
i. Restituire true se e solo se !’array contiene due elementi adiacenti duplicati.
j. Restituire true se e solo se !’array contiene elementi duplicati (non necessariamente adiacenti).

E7.11. Nella classe seguente


public class Sequence
{
private int[] values;
public Sequence(int size) { values = new int[size]; }
public void set(int i, int n) { values[i] = n; }
public int get(int i) { return values[i]; }
public int size() { return values.length; }
}
aggiungete il metodo
public boolean equals(Sequence other)

che verifichi se due sequenze contengono gli stessi valori, nello stesso ordine.

E7.12. Aggiungete alla classe Sequence dell’esercizio precedente il metodo


public boolean sameValues(Sequence other)

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.

E7.13. Aggiungete alla classe Sequence dell’Eserdzio E7.11 il metodo


public boolean isPennutationOf(Sequence other)

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

è una permutazione della sequenza

II 1 4 9 16 9 7 4 9

mentre la sequenza

I 4 9 16 9 7 4 9 11

non è una permutazione della sequenza

II 11 7 9 16 4 1 4 9

Probabilmente avrete bisogno di qualche metodo ausiliario.

E7.14. Aggiungete alla classe S e q u e n c e dell’Esercizio E7.11 il metodo


public Sequence sum(Sequence other)

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.

public class Table


{
private int[][] values;
public Table(int rows, int columns) { values = new int[rows![columns]; }
public void set(int i, int j, int n) { values[i]]j] « n; }
}
* * E7.17. Aggiungete alla classe Table dell’esercizio precedente il metodo
public double sum(int i, boolean horizontal)

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 ***********

E7.22. Nella classe seguente


public class Sequence
{
private ArrayList<Integer> values;
public SequenceO { values = new ArrayList<Integer>(); }
public void add(int n) { values.add(n); }
public String toStringO { return values.toStringO; }
}
412 C apitolo 7

a g g iu n g e te il m e to d o

public Sequence append(Sequence other)

c h e crei u n a n u o v a se q u e n z a , o tte n u ta a g g iu n g e n d o il c o n t e n u t o d ella se q u e n z a other in fo n d o


a q u e llo della se q u en za in esa m e, sen za m o d ific a r e n essu n a d e lle d u e. S e, ad e se m p io , a è la
se q u en za

1 4 9 16

e b è la seq u en za

9 7 4 9 11

allora l ’in v o c a z io n e a.append(b) restitu isce la seq u en za

1 4 9 16 9 7 4 9 11

sen za m o d ifica re a o b.

E 7 .2 3 . A g g iu n g e te alla classe Sequence d e ll’ese rc iz io p r e c e d e n te il m e to d o

public Sequence merge(Sequence other)

c h e restitu isca un a se q u en za i c u i e le m e n ti sia n o presi a lter n a tiv a m en te d alle d u e s e q u e n z e


ela b o ra te. S e un a d e lle d u e se q u e n z e è p iù co rta d e ll’altra, d o p o aver te r m in a to l ’altern an za
b iso g n a a g g iu n g e r e al risu lta to gli e le m e n ti rim asti nella se q u e n z a p iù lu n g a . S e, ad e s e m p io , a
è la se q u en za

1 4 9 16

e b è la seq u en za

9 7 4 9 11

allora l ’in v o c a z io n e a.merge(b) restitu isce la seq u en za

1 9 4 7 9 4 16 9 11

sen za m o d ifica re a o b.

E 7 .2 4 . A g g iu n g e te alla classe Sequence d e ll’E sercizio E 7 .2 2 il m e to d o

public Sequence mergeSorted(Sequence other)

c h e fon d a d u e se q u e n z e ord in ate, g e n e ra n d o un a seq u en za ordinata. U sa te u n in d ic e p er ciascu na


se q u en za , p er ten ere traccia della p o r z io n e c h e è già stata elaborata. A d o g n i passo a g g iu n g ere
alla se q u en za in c o str u z io n e l ’e le m e n to m in im o tra q u elli n o n ancora elab orati, p r e n d e n d o lo da
una d e lle d u e se q u e n z e di partenza, in c r e m e n ta n d o l ’in d ic e di c o n se g u e n z a . Se, ad e se m p io , a è la
seq u en za

1 4 9 16
A rray e vettori 413

e b è la sequenza

4 7 9 9 11

allora l’invocazione a.mergeSorted(b) restituisce la sequenza

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.

Esercizi di riepilogo e approfondimento


* R8.1. Analizzate un sistema di carsharing nel quale i guidatori di automobile possono accogliere
passeggeri a bordo, guadagnando denaro durante i propri spostamenti e riducendo il traffico. I
passeggeri attendono in appositi punti di prelevamento (pickuppoint), vengono scaricati alle loro
destinazioni e pagano per la distanza percorsa, mentre i guidatori vengono pagati una volta al
mese. Una app consente a guidatori e passeggeri di inserire i dati relativi al proprio percorso e ai
tempi coinvolti. La stessa app invia opportune notifiche a guidatori e passeggeri, oltre a gestire i
pagamenti. Individuate le classi che potrebbero essere utili per progettare tale sistema.
^ R8.2. Immaginate di voler progettare un social network per gestire i progetti di tirocinio nella vo­
stra università. Lo sponsor di un progetto lo descrive, specificando le capacità richieste, la data in
cui desidera che sia portato a termine e il lavoro che presume sia necessario svolgere. Gli studenti
usano un’applicazione di ricerca che consente loro di trovare progetti corrispondenti alle proprie
capacità e disponibilità. Individuate le classi che potrebbero essere utili per progettare tale sistema.
R8.3. Vi viene assegnato il compito di scrivere un programma che simuli il comportamento di
un distributore automatico. Gli utenti selezionano un prodotto e pagano: se le monete inserite
sono sufficienti a raggiungere il prezzo di acquisto del prodotto, questo viene fornito insieme
all’eventuale resto, altrimenti le monete inserite vengono restituite all’utente. Fornite un nome
adeguato per la classe che implementa questo programma e i nomi di due classi che sarebbero
utili, spiegandone i motivi.
** R8.4. Vi viene assegnato il compito di scrivere un programma che acquisisca in ingresso il nome
e l’indirizzo di un cliente, seguito da un elenco di articoli acquistati e dei relativi prezzi, per poi
visualizzare la fattura corrispondente. Quali delle seguenti classi sarebbero utili per implementare
questo programma? Invoice (fattura), InvoicePrinter (visualizzatoredifattura), Piiotlnvoìce (visualizza
unafattura), InvoiceProgram (programmaperfatture).
R8.5. Vi viene assegnato il compito di scrivere un programma che elabori buste paga. Gli impiegati
ricevono la loro busta paga settimanalmente e vengono pagati per ogni ora lavorata in base alla
P rogettazione di classi 455

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 ) .

R 8 .9 . Da quali classi dipende la classe Integer della libreria standard?


R 8 .1 0 . Da quali classi dipende la classe Rectangle della libreria standard?
Catalogate i metodi della classe
R 8 .1 1 . Scanner usati in questo libro come metodi d’accesso o
modificatori.
R 8 .1 2 . Catalogate i metodi della classe Rectangle come metodi d’accesso o modificatori.
R 8 .1 3 . La classe Resistor dell’Esercizio P8.12 è mutabile o immutabile? Perché?
R 8 .1 4 . Quali delle seguenti classi sono immutabili?
a. Rectangle
b. String
c. Random

R 8 .1 5 . Quali delle seguenti classi sono immutabili?


a. PrintStream
b. Date
c. Integer

R8.16. Considerate il metodo seguente:


public class DataSet
{
/**
Legge numeri da uno scanner e li aggiunge a questo insieme.
^>param in un oggetto di tipo Scanner
*/
public void read(Scanner in) { . . . }

} * *

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

public void printO


{
System.out.println(name + " " + value);
}
public void print(PrintStream stream)
{
stream.println(name + " ” + value);
}
public String toStringO
{
return name + " + value;
}
}

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

private int sides;


private static Random generator = new Random();
public Die(int s) { . . . }
public int casto { . . . }
}
Tracciate un grafico degli oggetti in memoria che mostri questi tre dadi:
Die d4 = new Die(4);
Die d6 = new Die(6);
Die d8 = new Die(S);

Non dimenticate di rappresentare i valori delle variabili sides e generator.

* 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

public static int readlnt(


Scanner in. String prompt. String error, int min, int max)

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);

E S . 2 3 (grafica). R ip etete l’esercizio precedente progettando le classi LetterH, LetterE, LetterL


e LetterO, ciascuna con un costruttore che riceve un parametro di tipo Point2D.Double (il vertice
superiore sinistro del rettangolo che racchiude la lettera) e un m eto d o draw(Graphics2D g2). Q uale
solu zion e è più orientata agli oggetti?

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

public void turnLeftO


public void turnRightO
public void moveO
public Point getLocationO
public String getDirection()

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à

Obiettivi del tapitolo


• Imparare Tereditarietà
• Implementare sottoclassi che ereditino e sovrascrivano metodi della superclasse
• Comprendere il concetto di polimorfismo
• Capire il concetto di superclasse comune, Object, e conoscere i suoi metodi

Spesso oggetti di classi correlate condividono un comportamento comune: ad esempio,


automobili, biciclette e autobus sono tutti mezzi di trasporto. In questo capitolo vedrete
come usare il concetto di ereditarietà per esprimere la relazione esistente tra una classe
generica e una classe specializzata. Usando l’ereditarietà sarete in grado di condividere
codice tra classi diverse, fornendo servizi che potranno essere utilizzati da più classi.
464 C apitolo 9

9.1 Gerarchie di ereditarietà


Una sottoclasse ere d ita dati
Nella progettazione orientata agli oggetti, !’ereditarietà {inheritance) è una relazione
e com p o rtam en ti da una superclasse.
esistente tra una classe più generica, detta superclasse (superclass)^ e una più specifica e
specializzata, detta sottoclasse (subclass): h sottoclasse “eredita” i dati e i comportamenti
dalla propria superclasse. Consideriamo, come esempio, la relazione esistente tra diversi
tipi di veicoli, come descritta nella Figura 1.

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

Ogni automobile è un veicolo e le automobili condividono molte caratteristiche con tutti


gli altri veicoli, come la capacità di trasportare persone da un luogo a un altro. Diciamo,
quindi, che la classe Car (automobile) eredita le proprietà della classe V ehicle (veicolo). In
questa relazione la classe V ehicle è la superclasse e la classe Car è la sottoclasse. Nella Figura
2, superclasse e sottoclasse sono collegate da una freccia che punta verso la superclasse.
In un programma, l’uso dell’ereditarietà consente di riutilizzare codice invece di
ricopiarlo, un riutilizzo che assume due forme diverse. Innanzitutto, una sottoclasse eredita
i metodi della superclasse. Ad esempio, se la classe V ehicle ha un metodo drive, che consente
di guidare il veicolo in qualche modo, allora la sottoclasse Car eredita automaticamente
tale metodo, non c’è bisogno di ricopiarlo.
Ereditarietà 465

Figura 2
U n d ia g r a m m a
di e re d ita rie tà

La seconda forma è più sottile e consente di riutilizzare algoritmi che manipolano


oggetti di tipo Vehicle. Dato che un’automobile è un tipo particolare di veicolo, possiamo
far manipolare a uno di tali algoritmi anche un oggetto di tipo Car e tutto funzionerà
Un o g g e tto di una sottoclasse
correttamente. Il principio di sostituzione {substitution principle) afferma che si può
^ s e m p r e essere u tilizzato al posto
sempre usare un oggetto di una sottoclasse in un punto in cui è prevista la presenza di
di un o g g e tto della sua superclasse.
un oggetto della sua superclasse. Consideriamo, ad esempio, un metodo che riceve un
argomento di tipo Vehicle:

void processVehicle(Vehicle v)

Dato che Car è una sottoclasse di Vehicle, si può invocare tale metodo anche con un
oggetto di tipo Car:

Car myCar = new Car(. . .)>


processVehicle(myCar);

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:

• con spazi da riempire {fill-in-the-blank);


• a scelta (singola o multipla) tra risposte predefmite {single choice o multiple choice);
• numeriche {numeric, nelle quali è spesso accettabile anche una risposta approssimata,
ad esempio 1.33 è una risposta valida anche quando la risposta corretta sarebbe 4/3);
• aperta {free response).

La Figura 3 mostra una gerarchia di ereditarietà per questi tipi di domande.


In cima alla gerarchia troviamo il tipo Question: una generica “domanda” può
visualizzare il proprio testo e può verificare se una risposta data è quella corretta.

File Question.java

Una domanda con un testo e una risposta corretta.


*/
466 C apitolo 9

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

public class Question


{
private String text;
private String answer;

Costruisce una domanda con stringhe vuote come testo e risposta.


*/
public Qu estiono
{
text =
answer =
}

Imposta il testo della domanda.


@param questionText il testo della domanda
*/
public void setText(String questionText)
{
text = questionText;
}

Imposta la risposta corretta di questa domanda.


@param correctResponse la risposta corretta
*/
public void setAnswer(String correctResponse)
{
answer = correctResponse;
}

Verifica se la risposta data è quella corretta.


@param response la risposta da verificare
@return true se la risposta è corretta, false altrimenti
*/
public boolean checkAnswer(String response)
{
Ereditarietà 467

return response.equals(answer);
}

Visualizza (il testo di) questa domanda.


*/
public void displayO
{
System.ou t.println(text);
}
}

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.

File QuestionDemoI .java

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);

Question q = new Question();


q.setTextC'Who was the inventor of Dava?");
q.setAnswer("Dames Gosling");

q. display 0 ;
System.out.print("Your answer: ");
String response = in.nextLine();
System.out.println(q.checkAnswer(response));
}
}

Esecuzione del programma

Who was the inventor of Dava?


Your answer: Dames Gosling
true

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.

I Suggerimenti per la programmazione 9.1


Usare un'unica classe per variazioni di valori e Tereditarieta per variazioni di comportamento
Lo scopo delPereditarietà è quello di rappresentare oggetti aventi comportamenti diversi.
Quando si inizia a studiare l’ereditarietà si tende a usarla troppo spesso, creando molte
classi anche quando le diversità tra i loro oggetti potrebbero essere espresse con una
semplice variabile di esemplare.
Considerate un programma che tiene traccia dell’efficienza nel consumo di carburante
in una flotta di automobili, registrando le distanze percorse e l’entità dei rifornimenti.
Alcune automobili della flotta sono a motore ibrido. Si deve progettare la sottoclasse
HybridCar? Non in questa applicazione: quando si tratta di fare rifornimento e percorrere
chilometri i veicoli ibridi non si comportano in modo diverso dalle altre automobili,
hanno soltanto una migliore efficienza nel consumo di carburante. U n’unica classe Car,
con la variabile di esemplare

double milesPerCallon;

che memorizza l’efficienza in miglia per galloni, è assolutamente sufficiente.


Se, invece, dovete scrivere un programma che mostra come si riparano diversi tipi
di veicoli, allora ha senso utilizzare una classe HybridCar distinta, perché, in merito alle
riparazioni, le automobili ibride hanno un comportamento diverso dalle altre.

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.

Pur ereditando il comportamento dalla classe Question, la classe ChoieeQuestion deve


evidenziare queste tre differenze:

publie elass ChoieeOuestion extends Question


{
// questa variabile di esemplare viene aggiunta alla sottoelasse
private ArrayList<String> ehoiees;

// questo metodo viene aggiunto alla sottoelasse


publie void addChoiee(String ehoiee, boolean eorreet) { . . . }

// questo metodo sovraserive un metodo della superelasse


publie void di sp la yO { . . . }
}

La parola riservata extends definisce la relazione di ereditarietà. La Figura 5 mostra come


La parola riservata e x t e n d s

definisce la relazione di e re d ita rie tà .


si rappresentano in un diagramma UML i metodi e le variabili di esemplare quando una
classe eredita da un’altra.
470 C apitolo 9

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

La Figura 6 mostra la struttura interna di un oggetto di tipo ChoiceOuestion : ha le variabili


di esemplare che sono state definite nella superclasse Question (text e answer) e ne aggiunge
un’altra, choices.
Il metodo addChoice è specifico della classe ChoiceQuestion: lo si può applicare soltanto
a oggetti di tipo ChoiceQuestion e non a generici oggetti di tipo Question.
Al contrario, il metodo display è un metodo che già esisteva nella superclasse, ma la
sottoclasse lo sovrascrive, in modo che possa visualizzare correttamente le opzioni tra cui
scegliere la risposta.

Sintassi di Java 9.1 Dichiarazione di sottoclasse

Sintassi public class NomeSottoclasse extends NomeSuperclasse


{
v a r i a b i l i d i esemplare
metodi
ì

Esempio Sottoclasse Superclasse


/ /
Dichiarazione delle variabili public class ChoiceQuestion extends Question
La parola riservata extends
di esemplare che vengono t
aggiunte alla sottoclasse. ArrayList<String> choices; caratterizza l'ereditarietà.

Dichiarazione dei metodi ^public void addChoice(String choice, boolean correct) { . . . }

che vengono aggiunti


nella sottoclasse. public void displayO { . . . }

Dichiarazione dei metodi


che vengono sovrascr/ff/
nella sottoclasse.
E reditarietà 471

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'');

Tuttavia, le variabili di esemplare private della superclasse sono inaccessibili e soltanto i


metodi della superclasse vi possono accedere: la sottoclasse non ha, nei loro confronti,
maggiori diritti di accesso di qualsiasi altra classe.
Quindi, ad esempio, i metodi della sottoclasse ChoiceQuestion non possono accedere
direttamente alla variabile answer: devono usare i metodi dell’interfaccia pubblica della
classe Question per accedere ai suoi dati privati, esattamente come farebbe qualunque altro
metodo.
Per illustrare meglio questo punto, implementiamo il metodo addChoice, che riceve due
argomenti: l’opzione da aggiungere (che viene inserita in fondo alla lista delle opzioni)
e un valore booleano che indica se si tratta dell’opzione corretta. Ad esempio:

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".

public void addChoice(String choice, boolean correct)


{
choices.add(choice);
if (correct)
{
// converte choices.size() in stringa
String choicestring = "" + choices.size();
setAnswer(choicestring);
}
}

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.

public class Employee


{
private String name;
private double baseSalary;

public void setName(String newName) { . . . }


public void setBaseSalary(double newSalary) { •}
public String getName() { . . . }
public double getSalaryO { . . . }
}

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.

Errori comuni 9.1 ....... ...... _. , . _____ _


Duplicare variabili di esemplare della superclasse
Una sottoclasse non ha accesso alle variabili di esemplare private della superclasse.
E reditarietà 473

public ChoiceOuestion(String questionText)


{
text = questionText;
// ERRORE: cerca di accedere alla variabile privata della superclasse
}

Spesso i programmatori alle prime armi cercano di “risolvere” questo problema


aggiungendo alla sottoclasse un'altra variabile di esemplare avente lo stesso nome.
public class ChoiceOuestion extends Ouestion
{
private ArrayList<String> choices;
private String text; // NON FATELO

} * ’ *

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.

Errori comuni 9.2 ______________ _____ _____ . _


Confondere superclassi e sottoclassi
Se confrontate un oggetto di tipo ChoiceQuestion e un oggetto di tipo Question, scoprite che:

• 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.

L’oggetto di tipo ChoiceQuestion sembra essere decisamente superiore. Allora, perché


mai la classe ChoiceQuestion viene chiamata sottoclasse e la classe Question viene chiamata
superclasses
474 C apitolo 9

La terminologia super/sotto è mutuata dalla teoria degli insiemi. Osservate


l’insieme di tutte le domande: non tutte sono oggetti di tipo ChoiceOuestion, alcune di
loro sono domande di altro tipo, quindi l’insieme degli oggetti di tipo ChoiceOuestion
è un 50 ^^ oi ns ie me dell’insieme di tutti gli oggetti di tipo Question, e quest’ultimo è un
superìnsieme dell’insieme degli oggetti di tipo ChoiceQuestion. Gli oggetti più specializzati,
che appartengono al sottoinsieme, hanno uno stato più ricco di proprietà e hanno maggiori
potenzialità.

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

m e to d o della superclasse ne può


ereditato non è adeguato alle esigenze della sottoclasse, può essere sovrascritto {overridden),
estendere o sostituire la funzionalità.
specificandone una nuova implementazione nella sottoclasse.
Consideriamo il metodo display della classe ChoiceQuestion, che sovrascrive il metodo
display della superclasse in modo da visualizzare le diverse opzioni tra cui va scelta la
risposta. Questo metodo estende la funzionalità del metodo definito nella superclasse: ciò
significa che il metodo della sottoclasse esegue le stesse azioni previste dall’esecuzione
del metodo della superclasse (in questo caso, visualizza il testo della domanda) e fa anche
qualcosa in più (in questo caso, visualizza le opzioni). In altre situazioni,invece,un metodo
di una sottoclasse sostituisce la funzionalità del corrispondente metodo della superclasse,
realizzando un comportamento completamente diverso.
Passiamo ora aH’implementazione del metodo display della classe ChoiceQuestion. Il
metodo deve:

• Visualizzare il testo della domanda.


• Visualizzare le opzioni per la risposta.

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

// visualizza le risposte tra cui scegliere


for (int i = 0; i < choices.size(); i++)
{
int choiceNumber = i + l;
System.out.println(choiceNumber + ": " + choices.get(i));
}
}
}

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

} ‘ ‘

Dato che il parametro implicito t h is fa riferimento a un oggetto di tipo ChoiceOuestion


e nella classe ChoiceOuestion esiste un metodo d isp lay, sarà quello a essere invocato: ma è
il metodo che stiamo scrivendo! Scritto in quel modo, il metodo invocherebbe se stesso
indefinitamente.
Vale la pena di osservare che super, diversamente da t h i s , non è un riferimento a un
oggetto. Non esiste un oggetto della superclasse a sé stante, in qualche modo “associato”
a un oggetto della sottoclasse: semplicemente, quest’ultimo contiene direttamente le
variabili di esemplare della superclasse. Sintatticamente super è soltanto una parola
riservata del linguaggio che provoca forzatamente l’esecuzione del metodo della
superclasse.
Ecco il programma completo che consente di rispondere a un questionario
composto da due oggetti di tipo ChoiceOuestion. Li costruiamo e li passiamo, uno alla
volta, al metodo presentO uestion, che visualizza la domanda e verifica se l’utente ha
risposto correttamente.

Sintassi di Java 9.2 Invocazione di un metodo della superclasse


\
Sintassi super .nomeMetodoiparametri);

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) ;

ChoiceOuestion second = new ChoiceOuestion();


second.setText("In which country was the inventor of lava born?");
second.addChoice("Australia", false);
second.addChoice("Canada", true);
second.addChoice("Denmark", false);
second.addChoice("United States", false);

presentOuest ion (first ) ;


presentOuestion(second);
}

Presenta una domanda all^utente e ne verifica la risposta.


@param q la domanda
*/
public static void presentOuestion(ChoiceOuestion q)
{
q.displayO;
System.out.print("Your answer: ");
Scanner in = new Scanner(System.in);
String response = in.nextLine();
System.out.println(q.checkAnswer(response));
}
}

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

Costruisce una domanda ancora priva di risposte tra cui scegliere.


*/
public ChoiceOuestionO
{
choices = new ArrayList<String>();
}

Aggiunge alla domanda una risposta tra cui scegliere.


@param choice la risposta da aggiungere
@param correct true se questa è la risposta corretta, false altrimenti
*/
public void addChoice(String choice, boolean correct)
{
choices.add(choice);
if (correct)
{
// converte choices.size() in stringa
String choicestring = "" + choices.size();
setAnswer(choiceString);
}
}
public void displayO
{
// visualizza il testo della domanda
super. displayO;
// visualizza le risposte tra cui scegliere
for (int i = 0; i < choices.size(); i++)
{
int choiceNumber = i + 1;
System.out.println(choiceNumber + ": " + choices.get(i));
}
}
}

Esecuzione del programma

What was the original name of the Dava language?


I: *7
2: Duke
3: Oak
4: Gosling
Your answer: I
false
In which country was the inventor of Dava born?
I: Australia
2: Canada
3: Denmark
4: United States
Your answer: 2
true
478 C apitolo 9

Auto-valutazione
11. Cosa c’è di sbagliato nella seguente implementazione del metodo display?

public class ChoiceOuestion extends Question


{
public void display0
{
System.ou t.println(text);
for (int i = 0; i < choices.size(); i-H-)
{
int choiceNumber = i + I;
System.out.println(choiceNumber + '': " -i- choices.get(i));
}
}

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.

Errori comuni 9.3


Sovraccaricare accidentalmente
InJava due metodi possono avere lo stesso nome, ma devono avere parametri di tipi diversi.
Ad esempio, la classe PrintStream ha vari metodi di nome println, tra i quali

void println(int x)
E reditarietà 479

void println(String x)

Sono metodi diversi, ciascuno con la propria implementazione: il compilatore Java li


considera completamente indipendenti e solitamente diciamo che il nome println è
sovraccarico (overloaded). Si tratta di un fenomeno diverso dalla sovrascrittura, dove un
metodo di sottoclasse fornisce una diversa implementazione di un metodo della superclasse
avente gli stessi parametri.
Se avete intenzione di sovrascrivere un metodo ma specificate almeno un parametro
di tipo diverso, introducete accidentalmente nella classe un metodo sovraccarico. In questo
esempio

public class ChoiceOuestion extends Question


{
public void display(PrintStream out)
// NON sovrascrive il metodo display()
{
}
}

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.

Errori comuni 9.4


Dimenticare super nelUnvocazione di metodi della superclasse
Quando si estendono le funzionalità di un metodo di una superclasse, un errore comune
consiste nel dimenticarsi super. Ad esempio, per calcolare lo stipendio di un dirigente,
bisogna ispezionare il salario del sottostante oggetto Employee, per poi aggiungere il premio
di produzione:
public class Manager extends Employee
{
public double getSalaryO
{
double baseSalary = getSalaryO;
// Errore: doveva essere super.getSalaryO
return baseSalary + bonus;
}
}

Qui g e t S a l a r y O si riferisce al metodo getSalary applicato al parametro implicito del


metodo in esecuzione, che è di tipo Manager: la classe Manager ha un metodo getSalary,
che viene quindi invocato. Questa invocazione è ricorsiva e non terminerà mai:
480 C apitolo 9

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.

Argomenti avanzati 9.1 _____________ _______ ______________ ___


Invocare il costruttore della superclasse
Prendiamo in esame la procedura di costruzione di un oggetto di una sottoclasse. Un
Un costruttore di una sottoclasse
costruttore di una sottoclasse può inizializzare soltanto le variabili di esemplare definite
Invoca ilcostruttore della superclasse
senza parametri, a meno che non cl
nella sottoclasse stessa, ma anche le variabili di esemplare della superclasse devono essere
sia una diversa indicazione esplicita.
inizializzate: se non si fornisce esplicitamente un’indicazione diversa, le variabili di
esemplare della superclasse vengono inizializzate invocando il costruttore della superclasse
privo di parametri.
Per specificare, invece, che si vuole invocare un costruttore diverso, come primo
enunciato del costruttore della sottoclasse bisogna usare la parola riservata super, seguita
dai parametri da fornire al costruttore della superclasse, indicati fra parentesi tonde.
Supponiamo, ad esempio, che la superclasse Question abbia un costruttore che inizializza
Per invocare un costruttore
della superclasse in un costruttore
il testo della domanda. Ecco come un costruttore di una sottoclasse potrebbe invocare
di una sottoclasse siusa la parola
quel costruttore della superclasse:
riservata super come primo
public ChoiceOuestion(String questioText)
enunciato.
{
super(questionText);
choices = new ArrayList<String>();
}

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

Sintassi di Java 9.3 Invocazione di un costruttore della superclasse

Sintassi public N om eC lasse(Jipd)iPaiam etio nomeParametro, . . . )


{
super { p a ia m e tii );

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

può essere u tilizza to in ogni punto


che è previsto un oggetto di una superclasse:
del pro g ram m a che preveda
ChoiceOuestion second = new ChoiceQuestion();
la presenza di un rife rim e n to

alla sua superclasse. presentQuestion(second); // si può passare un oggetto ChoiceQuestion

Quando viene eseguito il metodo presentQuestion,! riferimenti memorizzati nelle variabili


second e q puntano al medesimo oggetto, di tipo ChoiceQuestion (Figura 7), però la variabile
q non conosce il tipo effettivo dell’oggetto a cui fa riferimento (Figura 8).
Dato che q è una variabile di tipo Question, la si può usare per invocare i metodi
display e checkAnswer, ma non il metodo addChoice, perché non è un metodo della
superclasse Question.
482 C apitolo 9

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:

q.displayO; // invoca Question.display oppure ChoiceQuestion.display ?

Quale dei due metodi display viene invocato? Se esaminate, nel seguito, ciò che viene
Q uando la m acchina v irtu a le invoca .

un m eto d o di esem plare, usa


visualizzato dall’esecuzione del programma che stiamo progettando, vedrete che il
I m etodo della classe a cui appartiene
metodo invocato dipende dal contenuto della variabile parametro q. Nel primo caso q si
il p a ram etro im plicito; si parla
riferisce a un oggetto Question, per cui viene invocato il metodo Question.display, ma nel
di “ricerca din am ica del metodo". secondo caso q si riferisce a un oggetto ChoiceQuestion, per cui viene invocato il metodo
ChoiceQuestion.display, che visualizza l’elenco delle opzioni.
In Java, l’invocazione di un metodo viene sempre determinata dal tipo effettivo
!polim orfism o ("avere m o lte form e")

ci consente di m anipolare oggetti


dell'oggetto con cui il metodo viene invocato, non dal tipo della variabile che contiene il
che hanno in com un e alcune
riferimento all’oggetto: si parla di ricerca dinam ica del m eto d o {dynamic method
fu n zio n a lità , anche se queste sono
lookup).
im p le m e n ta te in m o di diversi. La ricerca dinamica del metodo da eseguire ci consente di manipolare in modo
omogeneo oggetti che sono esemplari di classi diverse, una caratteristica che si chiama
polim orfism o (dal greco “multiforme”): chiediamo a più oggetti di assolvere a un
determinato compito e ciascuno lo fa a modo suo.
Ereditarietà 483

Il polimorfismo agevola l’evoluzione e l’estensione dei programmi. Immaginate di


voler utilizzare un nuovo tipo di domanda che richiede di effettuare un calcolo, per il
quale si vuole accettare anche una risposta approssimata.Tutto ciò che dobbiamo definire
è una nuova classe, NumericOuestion, che estenda Question e che abbia il proprio metodo
checkAnswer.A questo punto possiamo invocare la funzione presentQuestion più volte, con un
assortimento di domande normali, domande con opzioni a scelta singola e domande con
risposta di tipo numerico. Non c’è alcuna modifica da fare alla funzione presentQuestion!
Grazie alla ricerca dinamica dei metodi, le invocazioni dei metodi display e checkAnswer
selezionano automaticamente il metodo della classe giusta.

File QuestionDemoS.java

import java.util.Scanner;

Questo programma mostra un questionario con due tipi di domande.


*/
public class OuestionDemoB
{
public static void main(String[] args)
{
Question first = new Question();
first.setText("Who was the inventor of Bava?");
first.setAnswer("Barnes Gosling");

ChoiceQuestion second = new ChoiceQuestion();


second.setText("In which country was the inventor of Bava born?");
second.addChoice("Australia", false);
second.addChoice("Canada", true);
second.addChoice("Denmark", false);
second.addChoice("United States", false);

presentQuestion(first) ;
presentQuestion(second);
}

Presenta una domanda all'utente e ne verifica la risposta,


^param q la domanda
*/
public static void presentOuestion(Ouestion q)
{
q.displayO;
System.out.print("Your answer: ");
Scanner in = new Scanner(System.in);
String response = in.nextLine();
System.ou t.printIn(q .checkAnswer(response));
}
}

Esecuzione del programma


Who was the inventor of Bava?
Your answer: Bjarne Stroustrup
false
484 C apitolo 9

In which country was the inventor of Dava born?


I: Australia
2: Canada
3: Denmark
4: United States
Your answer: 2
true

■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;

17. Se è una variabile di tipo BankAccount che contiene un riferimento diverso da


account
nuli,che informazioni abbiamo in merito all’oggetto a cui si riferisce?
18. Dichiarate un array, quiz, che sia in grado di contenere sia oggetti Question sia oggetti
ChoiceQuestion.
19. Quale metodo viene effettivamente invocato nel seguente frammento di codice?
ChoiceQuestion cq = . . .; // un valore diverso da nuli
cq.displayO;

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.

r JjgomentlayanMti 9,2 ____


Ricerca dinamica e parametro implicito
Immaginiamo di aver aggiunto il metodo
_________ ____

presentQuestion
____ _

alla classe Question:


public void presentOuestionO
{
displayO;
System.out.print("Your answer: ");
Scanner in = new Scanner(System.in);
String response = in.nextLine();
System.out.println(checkAnswer(response));
}

e analizziamo l’invocazione seguente:


ChoiceQuestion cq = new ChoiceQuestion();
cq.setTextC'In which country was the inventor of Dava born?");

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:

public class Question


{
public void presentOuestion0
{
this.displayO;
System.out.print("Your answer: ");
Scanner in = new Scanner(System.in);
String response = in.nextLine();
System.out.println(this.checkAnswer(response));
}
}

Nella nostra invocazione, il parametro implicito this è un riferimento a un oggetto di tipo


ChoiceOuestion. Per effetto della ricerca dinamica dei metodi, verranno automaticamente
invocate le versioni dei metodi display e checkAnswer definite nella classe ChoiceQuestion.
Tutto ciò accade anche se il metodo presentOuestion è stato dichiarato nella classe Question,
che non ha alcuna consapevolezza nemmeno dell’esistenza della classe ChoiceQuestion!
Come potete vedere, il polimorfismo è un meccanismo davvero molto potente. La
classe Question rende disponibile un metodo, presentQuestion, che specifica gli elementi
comuni che riguardano la visualizzazione di una domanda, cioè il fatto di visualizzarne il
testo e di acquisire la risposta dell’utente, verificandone la correttezza. Come si faccia, in
concreto, a visualizzare un particolare tipo di domanda e a verificare la correttezza della
corrispondente risposta è un compito che viene demandato alle sottoclassi.

Argorasnii avanatl2J_..™„___ ________ ___________ _________ ______


Classi astratte
Quando estendete una classe esistente, potete scegliere se ridefmire o meno i metodi
della superclasse, ma, talvolta, si vogliono obbligare i programmatori a ridefmire un metodo.
Questo avviene quando non esiste una buona soluzione predefinita da realizzare nella
superclasse e solo i programmatori della sottoclasse possono sapere come implementare
il metodo nel modo appropriato.
Ecco un esempio. Immaginate che la “First National Bank ofjava” decida che ciascun
tipo di conto bancario debba prevedere il pagamento di una commissione mensile. Di
conseguenza, aggiungiamo alla classe Account il metodo deductFees, che abbia il compito
di prelevare tale commissione:
public class Account
{
public void deductFees0 { . . . }

} ’ * *

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

dimenticarsi di sovrascrivere il metodo deductFees e, di conseguenza, il nuovo tipo di conto


erediterebbe dalla superclasse il metodo che non fa nulla. Esiste una strategia migliore:
dichiarare che deductFees è un m etodo astratto (abstract method), in questo modo

public abstract void deductFees();

Un metodo astratto non ha implementazione, obbligando così i programmatori di


sottoclassi a specificare implementazioni concrete per questo metodo (naturalmente in
alcune sottoclassi si può decidere di implementare un metodo che non fa nulla, ma si
tratta di una scelta deliberata, non di un’impostazione predefinita che viene ereditata
silenziosamente).
Non potete costruire oggetti di classi aventi metodi astratti. Per esempio, se la classe
Account ha un metodo astratto, il compilatore segnala un errore nel momento in cui si
tenta di crearne un esemplare, con new Account().
Una classe di cui non potete costruire esemplari è detta classe astratta, mentre
una classe in cui potete farlo viene detta talvolta classe concreta. In Java, tutte le classi
astratte devono essere dichiarate con la parola riservata abstract:
public abstract class Account
{
public abstract void deductFees();

} * * ’

public class SavingsAccount extends Account // classe non astratta


{
public void deductFees0 // implementazione
{
}
}

Se una classe estende una classe astratta senza fornire un’implementazione di tutti i metodi
astratti è anch’essa astratta.

public abstract class BusinessAccount extends Account


{
// nessuna implementazione di deductFees
}

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

Le classi astratte servono a obbligare i programmatori a creare sottoclassi: dichiarando


che alcuni metodi sono astratti, si evita di avere metodi predefmiti inutili, che potrebbero
essere ereditati per errore.

Argomenti avanzatiiA — ™ ______________ __________ _ _______


Metodi e classi final
In Argomenti avanzati 9.3 avete visto come potete obbligare altri programmatori che
creano sottoclassi di classi astratte a sovrascriverne i metodi astratti. Occasionalmente,
potreste desiderare il contrario e voler impedire la creazione di sottoclassi o la sovrascrittura
di determinati metodi: usate la parola riservata final. Per esempio, nella libreria standard
di Java la classe String è stata dichiarata in questo modo:
public final class String { }

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:

public class SecureAccount extends BankAccount


{
public final boolean checkPassword(String password)
{
} ’ * *

In questo modo nessuno potrà sovrascrivere il metodo checkPassword con un altro metodo
che restituisca semplicemente true.

Argomenti avanzati 9.5 __________________________ ______________ _


Accesso protetto
Nel tentativo di realizzare il metodo display della classe ChoiceQuestion abbiamo incontrato
una serie di difficoltà, perché tale metodo aveva bisogno di accedere alla variabile di
esemplare text della superclasse: la nostra soluzione ha usato i metodi appropriati della
superclasse per visualizzare il testo della domanda.
Java offre un’altra possibilità per risolvere questo problema. La superclasse può
dichiarare una variabile di esemplare protetta:
public class Question
{
protected String text;
488 C apitolo 9

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.

Consigli pratici 9 .1 ........ ... _ „ _ _ _ _____ __ _______ ___


Progettare una gerarchia di ereditarietà
Quando manipolerete un insieme di classi, alcune delle quali sono più generiche e altre
più specifiche, probabilmente vorrete organizzarle in una gerarchia di ereditarietà, perché
questo vi consentirà di elaborare in modo omogeneo oggetti di classi diverse.
Come esempio, consideriamo una banca che offra ai propri clienti i seguenti tipi di
conti:

• 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.

Problem a. Vogliamo realizzate un programma che dovrà gestire un insieme di conti


bancari di entrambi i tipi, strutturato in modo che vi si possano aggiungere altri tipi
di conto senza dover modificare il ciclo di gestione principale. L’utente deve avere a
disposizione un menu come questo:

D)eposit W)ithdraw M)onth end Q)uit

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.

Fase 1 Elencate le classi che fanno parte della gerarchia.


Nel nostro caso la descrizione del problema cita due classi: SavingsAccount e CheekingAeeount.
Ovviamente potremmo realizzarle separatamente, ma questa non sarebbe una buona idea,
perché nelle due classi dovremmo ripetere il codice relativo alle funzionalità comuni,
come l’aggiornamento del saldo di un conto: abbiamo bisogno di un’altra classe che si
accolli le responsabilità comuni, anche se la descrizione del problema non la menziona in
modo esplicito. Dobbiamo, quindi, scoprirla autonomamente, anche se in questo caso la
soluzione è semplice: i conti di risparmio e i conti correnti sono casi particolari di conti
bancari, quindi aggiungiamo la superclasse comune BankAccount.

Fase 2 Organizzate le classi in una gerarchia di ereditarietà.


Disegnate un diagramma di ereditarietà che evidenzi superclassi e sottoclassi. Ecco quello
relativo al nostro esempio:

Fase 3 Individuate le responsabilità comuni.


Nella Fase 2 avrete identificato la classe su cui si basa l’intera gerarchia: questa deve
avere responsabilità sufficienti per portare a termine i vari compiti che caratterizzano il
problema. Per scoprire quali siano questi compiti, scrivete lo pseudocodice che serve per
l’elaborazione degli oggetti.

Per ogni comando dell'utente


Se è un versamento o un prelievo
Versa o preleva !Importo usando il conto specificato.
Visualizza il saldo del conto coinvolto.
Se è "fine mese"
Perogniconto
Esegui l'elaborazione di fine mese.
Visualizza il saldo.

Esaminando lo pseudocodice, possiamo compilare la seguente lista di responsabilità


comuni, che riguardano qualsiasi conto bancario:
490 C apitolo 9

Versare denaro.
Prelevare denaro.
Ispezionare il saldo.
Eseguire l'elaborazione di fine mese.

Fase 4 Decidete quali metodi vanno sovrascritti nelle sottoclassi.


Per ogni sottoclasse e per ciascuna delle responsabilità comuni individuate nella Fase
3 bisogna decidere se il comportamento possa essere ereditato oppure se debba essere
sovrascritto. Ricordatevi di definire nella classe che sta alla base della gerarchia tutti i
metodi che dovranno essere ereditati o sovrascritti.

public c la ss BankAccount
{

Effettua un versamento in questo conto.


@param amount l'im porto da versare
*/
public void deposit(double amount) { . . . }

/**
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() ( . . . )
}

Entrambe le classi, SavingsAccount e CheckingAccount, sovrascrivono il metodo monthEnd. La


classe SavingsAccount deve anche sovrascrivere il metodo withdraw, per tenere traccia del
saldo minimo durante il mese; la classe CheckingAccount, invece, all’interno del metodo
withdraw deve aggiornare il contatore di transazioni.

Fase 5 Definite l’interfaccia pubblica di ciascuna sottoclasse.


Solitamente le sottoclassi hanno responsabilità diverse da quelle della superclasse: elencatele,
insieme ai metodi che devono sovrascrivere. Inoltre, dovete specificare come si costruiscono
gli oggetti delle sottoclassi.
In questo esempio ci serve un modo per impostare il tasso di interesse nei conti di
risparmio. Inoltre, dobbiamo specificare i costruttori e i metodi sovrascritti.
Ereditarietà 491

public c la ss SavingsAccount extends BankAccount


{

Costruisce un conto di risparmio con saldo uguale a zero.


*/
public SavingsAccountO { . . . }

Imposta i l tasso d 'in te re sse per questo conto.


@param rate i l tasso d 'in te re sse m ensile, in percentuale
*/
public void setInterestR ate(double rate) { . . . }

/ / qu esti metodi sovrascrivono i corrispondenti metodi d e lla superclasse


public void withdraw(double amount) { . . . }
public void monthEndO { . . . }

public c la ss CheckingAccount extends BankAccount


{

Costruisce un conto corrente con saldo uguale a zero.


*/
public CheckingAccount ( ) { . . . }

/ / qu esti metodi sovrascrivono i corrispondenti metodi d e lla superclasse


public void withdraw(double amount) { . . . }
public void monthEndO { • • • }
}

Fase 6 Identificate le variabili di esemplare.


Elencate le variabili di esemplare di ciascuna classe. Se trovate una variabile di esemplare
comune a tutte le classi, definitela nella classe che sta alla base della gerarchia.
Tutti i conti bancari hanno un saldo, quindi definiamo nella superclasse BankAccount
la variabile di esemplare balance:

public c la ss BankAccount
{
private double balance;

} * ’ *

La classe SavingsAccount ha bisogno di memorizzare il tasso di interesse e il saldo minimo


durante il mese, che viene aggiornato da tutti i prelievi.

public c la ss SavingsAccount extends BankAccount


{
private double interestR ate;
private double minBalance;

} * * *
492 C apitolo 9

La classe CheckingAccount deve contare le operazioni di prelievo, in modo da poter


applicare le commissioni dovute quando il loro numero supera il limite di prelievi
gratuiti.

public c la s s CheckingAccount extends BankAccount


{
private in t withdrawals;

} * ’ ’

Fase 7 Implementate costruttori e metodi.


I metodi della classe BankAccount aggiornano o restituiscono il saldo.
public void deposit(double amount)
{
balance = balance + amount;
}
public void withdraw(double amount)
{
balance = balance - amount;
}
public double getBalance()
{
return balance;
}

Progettando la superclasse BankAccount non possiamo ipotizzare quale sarà l’elaborazione


da svolgere a fine mese: decidiamo di definire un metodo che non faccia nulla:

public void monthEndO


{
}

Nel metodo withdraw della classe SavingsAccount viene aggiornato il saldo minimo raggiunto
durante il mese. Si noti l’invocazione dell’omonimo metodo della superclasse:

public void withdraw(double amount)


{
super.withdraw(amount);
double balance * getBalance();
i f (balance < minBalance)
{
minBalance = balance;
}
}

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);
}
}

L’elaborazione svolta a fine mese da un conto corrente consiste semplicemente


nell’azzeramento del contatore di prelievi:

public void monthEndO


{
withdrawals = 0;
}

Fase 8 Costruite oggetti di classi diverse e realizzate un collaudo.


Nel nostro programma d’esempio creiamo cinque conti correnti e cinque conti di
risparmio, memorizzando i loro riferimenti in una lista di conti bancari. Poi, eseguiamo
un ciclo che accetta comandi dall’utente per effettuare versamenti, prelievi ed elaborazioni
di fine mese.
BankAccount[] accounts = . . .;

Scanner in = new Scanner(System.in);


boolean done = fa lse ;
while (!done)
{
System.o u t.p r in t("D)eposit W)ithdraw M)onth end 0 )u it: '');
String input = in .n e x t();
i f (input.equals("D") || input.equals("W")) / / versamento o prelievo
{
System.out.print("Enter account number and amount: ");
in t num = in .n e x tln t();
double amount = in.nextDouble();

i f (input.equals("D'')) { accounts[num].deposit(amount); }
e ls e { accounts[num].withdraw(amount); }

System .out.println("Balance: " + accounts[num].getBalance();


}
e ls e i f (input.equals("M ")) / / elaborazione di fine mese
{
for (in t n = 0; n < accounts.length; n++)
{
accounts[n].monthEnd();
System .out.printIn(n + " " + accou nts[n ].getB alan ce());
}
}
e ls e i f (input.equals("Q "))
{
done = true;
}

Esempi com alfiliiJL- _____


6 Progettare una gerarchia di dipendenti per l'elaborazione delle buste paga
Problema. Vogliamo implementare Telaborazione delle buste paga per diverse categorie
di dipendenti:

• 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).

Il programma dovrà calcolare la retribuzione dovuta a un insieme di dipendenti. Per ogni


dipendente, chiede all’utente il numero di ore lavorate in una determinata settimana e
visualizza !’importo da pagare.

Fase 1 Elencate le classi che fanno parte della gerarchia.


La descrizione del problema elenca tre classi: HourlyEmployee, SalariedEmployee e Manager.
Inoltre, abbiamo bisogno di una classe che descriva le caratteristiche comuni alle tre
categorie di dipendenti: Employee.

Fase 2 Organizzate le classi in una gerarchia di ereditarietà.


Ecco il diagramma di ereditarietà per le nostre classi:
Fase 3 Individuate le responsabilità comuni.
Per scoprire le responsabilità comuni, scriviamo lo pseudocodice che serve per
l’elaborazione degli oggetti.

Perognidipendente
Visualizza il nome del dipendente.
Acquisisci il numero di ore lavorate.
Calcola la paga dovuta per quelle ore.

Esaminando lo pseudocodice possiamo compilare la seguente lista di responsabilità comuni,


che riguardano qualsiasi dipendente e, quindi, saranno attribuite alla classe Employee:

Ispezionare il nome.
Calcolare la paga dovuta per un dato numero di ore.

Fase 4 Decidete quali metodi vanno sovrascritti nelle sottoclassi.


In questo esempio non c’è bisogno di alcuna variante nell’ispezione del nome del
dipendente, ma la paga dovuta va calcolata in modo diverso da ciascuna sottoclasse, quindi
il metodo weeklyPay sarà sovrascritto da tutte:
/♦*
Un dipendente ha un nome e una stra teg ia
per calcolare la paga settim anale.
*/
public c la ss Employee
{
/**
Ispeziona i l nome di questo dipendente,
^return i l nome
*/
public String getName() { . . . }
Calcola l'im porto dovuto per una settimana di lavoro.
@param hoursWorked i l numero di ore lavorate n ella settimana
@return l'im porto dovuto per la settimana di lavoro
*/
public double weeklyPay(int hoursWorked) { . . . }
}

Fase 5 Definite l’interfaccia pubblica di ciascuna sottoclasse.


Costruiremo dipendenti fornendo il nome e le informazioni relative al salario.
public c la s s HourlyEmployee extends Employee
{
/**
Costruisce un impiegato a paga oraria
con un dato nome e un dato compenso orario.
*/
public HourlyEmployee(String name, double wage) {
}
public c la s s SalariedEmployee extends Employee
{

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
{

Costruisce un d irigen te con un dato nome,


un dato sa la rio annuo e un dato bonus settim anale.
*/
public Manager(String name, double salary, double bonus) {

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) { . . . }

Vista la semplicità di questo esempio, non servono altri metodi.

Fase 6 Identificate le variabili di esemplare.


Tutti i dipendenti hanno un nome, per cui la classe Employee deve avere la variabile di
esemplare name (come si può vedere nella gerarchia aggiornata qui riportata).
Cosa possiamo dire in merito ai salari? I dipendenti “a ore” hanno una paga
oraria, mentre i dipendenti salariati hanno un salario annuo. Anche se sarebbe possibile
memorizzare entrambi questi valori in un’unica variabile di esemplare della superclasse,
non sarebbe una buona idea: il codice risultante sarebbe complesso e sarebbe facile fare
errori, perché dovrebbe dare un senso a quei numeri.
È preferibile che gli oggetti di tipo HourlyEmployee memorizzino la propria paga oraria,
mentre gli oggetti di tipo SalariedEmployee memorizzeranno il proprio salario annuo. Gli
oggetti di tipo Manager, poi, devono memorizzare anche il proprio premio di produzione
settimanale.

Fase 7 Implementate costruttori e metodi.


Nei costruttori delle sottoclassi dobbiamo ricordarci di assegnare un valore alle variabili
di esemplare della superclasse.
public SalariedEmployee(String name, double salary)
{
setName(name);
annualSalary = salary;
}

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 ^

public Manager(String name, double salary, double bonus)


{
super(name, salary);
weeklyBonus = bonus;

La paga settimanale può, poi, essere calcolata seguendo le istruzioni presenti nella
descrizione del problema:

public c la ss HourlyEmployee extends Employee


{
public double weeklyPay(int hoursWorked)
{
double pay = hoursWorked * hourIyWage;
i f (hoursWorked > 4 0 )
{
pay = pay + ((hoursWorked - 4 0 ) * 0 . 5 ) * hourlyWage;
}
return pay;
}
}
public c la s s SalariedEmployee extends Employee
{
public double weeklyPay(int hoursWorked)
{
final in t WEEKS_PER_YEAR = 52;
return annualSalary / WEEKS_PER_YEAR;
}
}

Nel caso della classe Manager dobbiamo invocare il metodo omonimo della superclasse
SalariedEmployee:

public c la s s Manager extends SalariedEmployee


{
public double weeklyPay(int hoursWorked)
{
return super.weeklyPay(hoursWorked) + weeklyBonus;
}

Fase 8 Costruite oggetti di classi diverse e realizzate un collaudo.


Nel nostro programma d’esempio popoliamo una lista di dipendenti e calcoliamo le
paghe settimanali:
Employee[] s t a f f = new Employee[3];
s ta ff[0 ] = new HourlyEmployee("Morgan, Harry", 30);
s ta ff[l] = new SalariedEmployee("Lin, Sally", 52000);
s t a f f [2 ] = new Manager("Smith, Mary", 104000, 50);
tREDITARIETA

Scanner in = new Scanner(System.in);


for (Employee e : s t a f f )
{
System.out.print("Hours worked by " + e.getName() + ": ");
in t hours = in .n e x tI n t();
System .out.println("Salary: " + e.weeklyPay(hours));
}

Nel pacchetto dei file scaricabili per questo libro, la cartella worked_example_i del Capitolo
9 contiene il codice sorgente completo delle classi.

9.5 La superclassfi uni¥ersale:i)bject


In Java, ogni classe che venga dichiarata senza una esplicita clausola extends estende
automaticamente la classe Object, quindi in Java la classe Object è la superclasse, diretta o
indiretta, di tutte le classi (osservate la Figura 9). La classe Object definisce alcuni metodi
molto generici, tra i quali citiamo:

• che restituisce una stringa che descrive !’oggetto (Paragrafo 9.5.1);


toS trin g,
• equals,che confronta tra loro due oggetti (Paragrafo 9.5.2);
• hashCode, che restituisce un codice numerico utile per memorizzare oggetti in un
insieme (come si vedrà nella sezione Argomenti avanzati 14.1).

Figura 9
La classe Object è la
superclasse di tu tte le
classi Java

9.5.1 Sovrascrivere il metodo to S trin g


Il metodo toString restituisce una rappresentazione in forma di stringa per ciascun oggetto
ed è spesso utilizzato durante il debugging.
Esaminiamo, come esempio, la classe Rectangle della libreria standard di Java. Il suo
metodo toString restituisce una stringa che contiene lo stato del rettangolo:

Rectangle box = new Rectangle(5, 10, 20, 30);


String S = box.toStringO ;
// S diventa "java.awt.Rectangle[x=5,y=l0,width=20,height=30]"
500 C apitolo 9

Questo metodo toS trin g viene invocato automaticamente tutte le volte che concatenate
una stringa con un oggetto. Esaminate questa concatenazione:
"box=" + box;

A un lato dell’operatore di concatenazione + troviamo una stringa, ma all’altro lato c’è un


riferimento a un oggetto. Il compilatore Java invoca automaticamente il metodo toS trin g
per “trasformare” tale oggetto in una stringa e, successivamente, le due stringhe vengono
concatenate. In questo caso, il risultato è costituito da questa stringa:
"box=java. awt. Rectangle[x=5,y=l0,width=20,height=30]"

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:

BankAccount mofnsSavings » new BankAccount( 5000);


String S * momsSavings.toStringO;
/ / S diventa sim ile a "BankAccount@d24606bf"

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

Ora funziona meglio:

BankAccount tnomsSavings = new BankAeeount(SOOO);


String S = momsSavings.toStringO;
/ / S diventa "BankAeeount[balanee=5000]"

9.5.2 II metodo equals


Oltre al metodo toS trin g, la classe Objeet definisce anche il metodo equals, il cui compito
Il metodo e q u a ls verifica se due
i | p H hanno lo stesso contenuto.
è quello di verificare se due oggetti hanno lo stesso contenuto:
i f (stam pi.equals(stam p2)) . . .
/ / hanno id en tico contenuto, come n ella Figura 10

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

Realizziamo il metodo equals nella classe Stamp {francobollò), sovrascrivendo il metodo


equals della classe Object:
502 C apitolo 9

public c la ss Stamp
{
private String color;
private in t value;

public boolean equals(Object otherObject)


{
}

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;

Ora potete confrontare i due francobolli:


public boolean equals(0bject otherObject)
{
Stamp other = (Stamp) otherObject;
return c o lo r .eq u als(oth er.color) && value == other.value;
}

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.

9.5.3 L'operatore in stan ceof


Come avete visto, è possibile memorizzare un riferimento di tipo sottoclasse in una
variabile di tipo superclasse:

ChoiceOuestion cq = new ChoiceOuestion();


Question q = cq; / / va bene
Object obj = cq; / / va bene

Più raramente dovrete eseguire la conversione inversa, assegnando un riferimento di tipo


superclasse a una variabile di tipo sottoclasse.
^ Ad esempio, potrebbe esserci una variabile obj di tipo Object, ma potreste sapere con
Se sapete che un oggetto
è un esemplare di una determinata
certezza che, in effetti, contiene un riferimento a un oggetto di tipo Question. In tal caso
classe, potete usare un cast
potreste usare un cast per cambiarne il tipo:
per convertirlo in quel tipo.
Question q = (Question) obj;

Questa conversione mediante cast, però, è in qualche modo pericolosa: se vi sbaglia­


te e, invece, obj fa riferimento a un oggetto di tipo diverso, verrà lanciata un’eccezione di
tipo “class cast”, cioè un errore in fase di esecuzione relativo alla conversione tra classi.
Ereditarietà 503

Sintassi di Java 9.4 L'operatore in s ta n c e o f

Sintassi oggetto instanceof NomeTipo

Esempio Restituisce true se anob je c t può essere


convertito in Question.
Se anObject vale n u li, in sta n ceo f
restituisce fa ls e . L'oggetto può appartenere a una
i f (anObject instanceof Question)
'sottoclasse di Question.
{
Question q = (Question) anObject;

Due riferim enti ai


Con questa variabile si possono
medesimo oggetto.
invocare metodi di Question.

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:

i f (obj instan ceof Question)


{
Question q = (Question) obj;
}

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:

i f (q instan ceof ChoiceQuestion) / / non fa te così! Errori comuni 9.5


{
/ / elaborazione prevista per una domanda di tip o ChoiceQuestion
}
e ls e i f (q instanceof Question)
{
/ / elaborazione prevista per una domanda di tip o Question
}
504 C apitolo 9

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.

Errori comuni 9.5


Non usate verifiche per i tipi
Alcuni programmatori hanno l’abitudine di usare verifiche per i tipi, in modo da poter
realizzare comportamenti variabili per ciascuna classe:

if (q instanceof ChoiceOuestion) // non fate così!


{
// elaborazione prevista per una domanda di tipo ChoiceOuestion
}
else if (q instanceof Ouestion)
{
// elaborazione prevista per una domanda di tipo Question
}

È 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:

else if (q instanceof NumericOuestion)


{
Ereditarietà 505

11 elaborazione prevista per una domanda di tip o NumericOuestion


}

Pensiamo, invece, a cosa succede se aggiungiamo la classe NumericOuestion al nostro


programma che gestisce questionari: non dobbiamofare alcuna modifica, perché il programma
usa il polimorfismo, non la verifica dei tipi.
Ogni volta che state per scrivere una verifica dei tipi all’interno di una gerarchia
di classi, ripensate al vostro progetto e usate il polimorfismo. Dichiarate un metodo, ad
esempio doTheTask, nella superclasse, per poi sovrascriverlo nelle sottoclassi; l’invocazione,
poi, diventa:

q.doTheTaskO;

Argomenti avanzati 9.6


Pereditarietà e il metodo to S tr in g
Avete già visto come scrivere un metodo toString: componete una stringa formata dal
nome della classe e dalle coppie nome=valore delle variabili di esemplare, separate da
virgole. Se, però, volete che il vostro metodo toS trin g sia utilizzabile dalle sottoclassi della
vostra classe, dovete lavorare un po’ di più.
Invece di scrivere esplicitamente nel metodo il nome della classe, dovreste invocare il
metodo getC lass (ereditato dalla classe Object) per ottenere un oggetto che descrive una
classe e le sue proprietà. Quindi, invocatene il metodo getName per ottenere il nome della
classe:
public String toStringO
{
return getC lass().getN am e() + "[balance='' + balance +
}

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]"

Ovviamente dovreste sovrascrivere to S tr in g anche nella sottoclasse SavingsA ccount,


aggiungendo nomi e valori delle variabili di esemplare proprie della sottoclasse stessa.
Notate che dovete invocare su p er.toS trin g per ottenere le variabili di esemplare della
superclasse, dato che la sottoclasse non vi può accedere direttamente:
public c la ss SavingsAccount extends BankAccount
{
public String toStringO
{
return super.toStringO + "[interestRate=" + interestR ate +
}
506 C apitolo 9

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.

Argomenti avanzati 9.7 __ __ _______________ _____________ ____


L'ereditarietà e il metodo equals
Avete appena visto come scrivere un metodo equals: eseguite un cast sulla variabile
parametro otherO b ject, in modo da trasformarla nel tipo della vostra classe, quindi
confrontate le variabili di esemplare del parametro implicito e del parametro esplicito
convertito.
Ma cosa succede se qualcuno invoca stam p i.eq u als(x ), ma x non è un oggetto di tipo
Stamp? Il cast errato genera un’eccezione e il programma termina bruscamente: è bene
verificare che otherObject sia realmente un esemplare della classe Stamp. Il controllo più
semplice si farebbe utilizzando l’operatore in sta n c e o f , ma tale verifica non è abbastanza
specifica, perché darebbe esito positivo anche se otherObject appartenesse a una sottoclasse
di Stamp. Per eliminare tale possibilità, occorre verificare che i due oggetti appartengano
alla stessa classe. In caso contrario, restituite f a ls e .
i f (getC lassO != otherO b ject.getC lass()) { return fa ls e ; }

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;

public boolean equals(Object otherObject)


{
i f (!super.equals(otherO bject)) { return fa ls e ; }
CollectibleStamp other = (CollectibleStam p) otherObject;
return year == other.year;
}
Ereditarietà 507

Computer e società 9.1


AAJL
Chi controlla Internet? questo insieme di protocolli, oggi Internet ha una struttura forte­
Nel 1962J.C.R. Licklider era a capo denominato TCP/IP (Transmission mente democratica. Chiunque può
del primo programma di ricerca sui Control Protocol / Internet Protocol). pubblicare qualsiasi cosa e può legge­
calcolatori presso DARPA (Defense Il primo gennaio 1983 tutti i com­ re tutto ciò che viene pubblicato da
Advanced Research Projects Agency, puter connessi a Internet passarono altri: un fenomeno che spesso non va
Agenzia per i progetti di ricerca contemporaneamente all’utilizzo di d’accordo con i governi e le aziende.
avanzata del Ministero della Difesa TCP/IP, in uso ancora oggi. Molti governi, infatti, control­
degli Stati Uniti d’America) e in Nel tempo, divenne disponibile lano !’infrastruttura di Internet nel
quegli anni pubblicò una serie di sulla rete Internet una quantità sem­ proprio Paese. Ad esempio, un utente
articoli che descrivevano una “rete pre maggiore di informazione, creata di Internet in Cina che faccia una
a galassie”, tramite la quale gli utenti da ricercatori e amatori. Ad esempio, ricerca sul massacro di Piazza Tie-
dei computer avrebbero potuto ac­ il Progetto Gutenberg (www.gutenberg. nanmen o sull’inquinamento nella
org) rende disponibile in formato propria città, potrebbe non ottenere
cedere ai dati e ai programmi pre­
elettronico il testo di importanti libri alcun risultato. Il Vietnam blocca
senti in siti diversi da quelli in cui
classici, i cui diritti d’autore siano l’accesso a Facebook, probabilmente
lavoravano: stiamo parlando di un
scaduti. Nel 1989,Tim Berners-Lee nel timore che i dissidenti anti­
periodo molto precedente a quello
iniziò il suo lavoro sui documenti governativi lo possano utilizzare per
in cui furono inventate le reti per cal­
ipertestuali, che consentono agli organizzarsi meglio. Il governo statu­
colatori. Nel 1969, quattro calcolatori
utenti una consultazione arricchita
(tre in California e uno nello Utah) nitense ha preteso che le biblioteche
da collegamenti con documenti cor­
vennero connessi ad ARPANET, e le scuole a sovvenzione pubblica
relati: !’infrastruttura derivante è oggi
che precorse Internet. La rete crebbe installassero filtri per bloccare infor­
nota con il nome di WorldWideWeb
rapidamente, collegando computer di mazioni sessualmente esplicite o di
{ragnatela mondiale, W W W ).
varie università e organizzazioni di ri­ istigazione all’odio; inoltre, le orga­
Le prime interfacce per accedere
cerca, e all’inizio si pensò che la mag­ nizzazioni per la sicurezza nazionale
a queste informazioni erano, rispetto
gior parte dei ricercatori !’avrebbe hanno spiato !’utilizzo di Internet da
agli standard di oggi, incredibilmente
utilizzata per eseguire programmi su parte dei cittadini.
confuse e diffìcili da usare e, nel mar­
computer diversi dal proprio: usando zo 1993, il traffico dovuto al sistema Nei casi in cui il servizio In­
l’esecuzione remota, un ricercatore di WWW era lo 0.1% del traffico totale ternet viene fornito da aziende te­
una istituzione avrebbe avuto accesso in Internet. Tutto ciò cambiò radi­ lefoniche O televisive, queste a volte
a un computer poco utilizzato che si calmente quando Marc Andersen, interferiscono con offerte di servizio
trovasse in una diversa località. Diven­ allora laureando al NCSA (National alternative: le compagnie che offrono
ne rapidamente evidente, però, che Center for Supercomputing Appli­ il servizio di telefonia cellulare spesso
l’esecuzione remota non era il motivo cations), progettò e rese disponibile si rifiutano di veicolare servizio di
prevalente di utilizzo della rete: Vap- Mosaic, un’applicazione che era in voice-over-lP (telefonia via Internet)
plicazione ^/7/erera la posta elettronica, grado di visualizzare pagine Web e le aziende di televisione via cavo
cioè lo scambio di messaggi fra utenti in forma grafica, usando immagini, rallentano la diffusione di video con
di computer situati in luoghi diversi. colori e diversi tipi di font di caratteri. tecnologia streaming.
Nel 1972, Bob Kahn propose Andersen divenne famoso e la sua La rete Internet è diventata un
di estendere ARPANET per creare fama si trasferì all’azienda che fondò, potente strumento per la diffusione
la rete Internet: un insieme di reti in­ Netscape; inoltre, Microsoft acquisì la dell’informazione, buona e cattiva: è
tercomunicanti. Tutte le reti apparte­ licenza di Mosaic per creare Internet nostra responsabilità, come cittadini,
nenti a Internet condivìdono protocolli Explorer. Nel 1996, il traffico WWW chiedere ai nostri governi di poter
comuni per la trasmissione dei dati: rappresentava più della metà dei dati controllare autonomamente l’accesso
Kahn e Vinton Cerf svilupparono trasportati dalla rete Internet. a tale informazione.
508 C apitolo 9

.Riepìlogo degli obiettivi dì appi;§Qdìn\i^p|9


Ereditarietà, superclasse e sottoclasse
Una sottoclasse eredita dati e comportamenti da una superclasse.
Un oggetto di una sottoclasse può sempre essere utilizzato al posto di un oggetto della sua
superclasse.
Implementare sottoclassi
Una sottoclasse eredita tutti i metodi che non sovrascrive.
Una sottoclasse può sovrascrivere un metodo ereditato dalla sua superclasse fornendone
una nuova implementazione.
La parola riservata extends definisce la relazione di ereditarietà.
Una sottoclasse può sovrascrivere metodi della sua superclasse
Un metodo che sovrascrive !’omonimo metodo della superclasse ne può estendere o
sostituire la funzionalità.
Per invocare un metodo della superclasse si usa la parola riservata super.
Un costruttore di una sottoclasse invoca il costruttore della superclasse senza parametri,
a meno che non ci sia una diversa indicazione esplicita.
Per invocare un costruttore della superclasse in un costruttore di una sottoclasse si usa la
parola riservata super come primo enunciato.
Un costruttore di una sottoclasse può fornire argomenti a un costruttore della superclasse
usando la parola riservata super.
Uso del polimorfismo per elaborare oggetti di tipi correlati
Un riferimento a una sottoclasse può essere utilizzato in ogni punto del programma che
preveda la presenza di un riferimento alla sua superclasse.
Quando la macchina virtuale invoca un metodo di esemplare, usa il metodo della classe a cui
appartiene il parametro implicito: si parla di “ricerca dinamica del metodo”.
11polimorfismo (“avere molte forme”) ci consente di manipolare oggetti che hanno in comune
alcune funzionalità, anche se queste sono implementate in modi diversi.
La classe object e i suoi metodi
Sovrascrivete il metodo toString in modo che restituisca una stringa che descrive lo stato
dell’oggetto.
11 metodo equals verifica se due oggetti hanno lo stesso contenuto.
Se sapete che un oggetto è un esemplare di una determinata classe, potete usare un cast
per convertirlo in quel tipo.
L’operatore instanceof verifica se un oggetto è di un determinato tipo.

J^^rcizi di riepilooo e approfondimento ...........


R9.1. Nella sezione Esempi completi 9.1:
a. quali so n o le sottoclassi di Employee?
b. quali so n o le superclass! di Manager?
c. quali so n o le superclass! e le sottoclassi di SalariedEmployee?
d. quali classi sovrascrivono il m e to d o weeklyPay della classe Employee?
Ereditarietà 509

e. quali classi sovrascrivono il metodo setName della classe Employee?


f. quali sono le variabili di esemplare di un oggetto di tipo HourlyEmployee?
R9.2. Nelle seguenti coppie di classi, individuate la superclasse e la sottoclasse:
a. Employee, Manager (c io è dipendente e dirigente)
b. CraduateStudent, Student {studente universitario e studente)
c. Person, Student (persona e studente)
d. Employee, Professor (dipendente e professore)
e. BankAccount, CheckingAccount (conto bancario e conto corrente bancario)
f. Vehiele, Car (veicolo e automobile)
g. Vehiele, Minivan (veicolo e automobilefamiliare)
h. Car, Minivan (automobile e automobilefamiliare)
i. Truek, Vehiele (autocarro e veicolo)

R9.3. In un programma che gestisce l’inventario in un negozio di piccoli elettrodomestici,


perché non è utile definire la superclasse SmallApplianee (piccolo elettrodomestico) con le sottoclassi
Toaster (tostapane), CarVaeuum (aspirapolvere per automobile), TravelIron (ferro da stiro da viaggio) e
così via?
R9.4. Quali metodi eredita dalla propria superclasse la classe ChoieeQuestion? Quali metodi
sovrascrive? Quali metodi aggiunge?
R9.5. Quali metodi eredita dalla propria superclasse la classe SavingsAeeount vista nella sezione
Consigli pratici 9.1? Quali metodi sovrascrive? Quali metodi aggiunge?
R9.6. Elencate le variabili di esemplare di un oggetto di tipo CheekingAeeount, classe definita
nella sezione Consigli pratici 9.1.
R9.7. Se la classe Sub estende la classe Sandwieh, quali fra le seguenti sono assegnazioni permesse
dopo aver eseguito i primi due enunciati?
Sandwich x = new Sandwich();
Sub y = new Sub();

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.10. Quali relazioni di ereditarietà stabilireste fra le classi seguenti?


Student (studente)
Professor (professore)
TeachingAssistant (assistente del professore)
Employee (dipendente)
Secretary (segretario)
DepartmentChair (direttore di dipartimento)
Janitor (bidello)
SeminarSpeaker (oratore di seminario)
Person (persona)
Course (corso)
Seminar (seminario)
Lecture (lezione)
ComputerLab (esercitazione in laboratorio)

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.

a. System.out instanceof PrintStream


b. System.out instanceof OutputStream
c. System.out instanceof LogStream
d. System.out instanceof Object
e. System.out instanceof String
f. System.out instanceof Writer

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ì:

The inventor of Dava was ___

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.

E9.16. Risolvete nuovamente l’esercizio precedente memorizzando la posizione del punto


in un oggetto di tipo java.aw t.Point. Il metodo toString deve invocare il metodo toString della
classe Point.
E9.17 (economia). Modificate la classe CheckingAccount vista nella sezione Consigli pratici
9.1 in modo che prelevi una commissione di $1 per ogni versamento o prelievo eseguito
oltre le tre transazioni mensili gratuite. Inserite il codice che calcola la commissione dovuta
in un metodo separato, che verrà invocato dai metodi deposit e withdraw.

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-*^

Obiettivi del capitolo


• Saper dichiarare e usare interfacce
• Utilizzare le interfacce per ridurre l’accoppiamento tra classi
• Imparare a realizzare classi ausiliarie e classi interne
• Realizzare ricevitori di eventi in applicazioni grafiche

Per migliorare la produttività dei programmatori si vuole poter riutilizzare componenti


software all’interno di più progetti. In questo capitolo apprenderete un’importante strategia
di programmazione che consente di separare le parti riutilizzabili di un’elaborazione
dalle parti che vanno modificate in relazione alla situazione in cui vengono riutilizzate.
La parte riutilizzabile invoca metodi di una interfaccia e lavora di concerto con una classe
che realizza i metodi dell’interfaccia stessa. Per realizzare una diversa applicazione basta
progettare una diversa classe che realizzi la medesima interfaccia: il comportamento del
programma si modifica in base a quello della classe che viene effettivamente utilizzata.
514 C apitolo 10

, Uso di interfac(SJiajl riutilizzo di a|flfi[ÌlinL


Quando si fornisce un servizio, solitamente lo si vuole rendere disponibile per una clientela
che sia la più vasta possibile. Un ristorante, ad esempio, serve i suoi clienti e, in Java, se
ne potrebbe realizzare un modello usando un metodo come questo:

public void serve(Person c lie n t)

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:

public void serve(Customer c lie n t)

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.

10.1.1 Individuare un tipo interfaccia


In questo paragrafo analizzeremo un servizio che calcola valori medi, con l’obiettivo
di fare in modo che sia il più generale possibile. Iniziamo da un’implementazione del
servizio che calcoli il saldo medio di un array di conti bancari:

public s ta tic double average(BankAccount[] ob jects)


{
double sum = 0;
for (BankAccount obj : ob jects)
{
sum = sum + obj.getB alance();
}
i f (o b jects.len g th > O) { return sum / ob jects.len gth ; }
e ls e { return 0; }
}
Interfacce 515

Immaginiamo, ora, di voler calcolare il valore medio di oggetti di altro tipo: dobbiamo
scrivere un altro metodo. Ecco quello che elabora oggetti Country:

public s ta tic double average(Country[] ob jects)


{
double sum = 0;
for (Country obj : ob jects)
{
sum = sum + obj.getA rea();
}
i f (o b jects.len g th > O) { return sum / o b jects.len gth ; }
e ls e { return 0; }
}

L’algoritmo che calcola il valore medio è, evidentemente, lo stesso in tutti i casi, ma


cambiano i dettagli della misurazione dei valori di cui fare la media: ci piacerebbe progettare
un unico metodo che fornisca questo servizio.
C ’è, però, un problema: ogni classe usa, in generale, un metodo di nome diverso
per consentire l’ispezione del valore di cui si vuol fare la media. Nella classe BankAccount
usiamo il metodo getBalance, mentre nella classe Country usiamo il metodo getArea.
Supponete che le diverse classi possano accordarsi sull’esistenza di un metodo,
getMeasure, che fornisca la misura da usare nell’analisi dei dati: per i conti bancari getMeasure
restituisce il saldo, per le nazioni restituisce la superficie, e così via.
In questo modo potremmo realizzare un unico metodo che calcoli:

sum = sum + obj.getMeasure();

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:

BankAccount oppure Country oppure . . . obj; / / non s i può

Dobbiamo inventare un nuovo tipo che descriva qualsiasi classe i cui oggetti possano
essere “misurati”: nel prossimo paragrafo vedremo come farlo.

10.1.2 Dichiarare un tipo interfaccia


In Java un tipo interfaccia dichiara In Java, un tipo interfaccia {interface type) viene utilizzato per specificare un elenco di
!metodi che possono essere invocati operazioni necessarie. La dichiarazione è simile a quella di una classe: si elencano i metodi
con una variabile di quel tipo. richiesti dall’interfaccia, senza, però, fornire !’implementazione di tali metodi. Ecco, ad
esempio, la definizione di un tipo interfaccia che chiamiamo Measurable, cioè “misurabile”:
public in terface Measurable
{
double getMeasureO;
}

L’interfaccia Measurable richiede un solo metodo, getMeasure, ma, in generale, ne può


richiedere più d’uno.
516 C apitolo IO

Sintassi di Java 10.1 Dichiarazione di interfaccia



A
Sintassi public in terface NomeInterfaccia
{
intestazioni dei metodi
}

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:

• U n’interfaccia non ha variabili di esemplare.


• 1 metodi di un’interfaccia devono essere astratti, cioè non hanno un’implementazione,
oppure, a partire da Java 8, possono essere metodi s t a t i c o d efa u lt (sezioni Note per
JavaS 10.1 e 10.2)
• Tutti i metodi di un’interfaccia sono automaticamente pubblici.
• U n’interfaccia non ha costruttori: le interfacce non sono classi e non si possono
costruire oggetti di tipo interfaccia.

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:

public s t a t ic double average(Measurable[] ob jects)


{
double sum = 0;
for (Measurable obj : ob jects)
{
sum = sum + obj.getM easure();
}
i f (o b jects.len g th > 0) { return sum / ob jects.len gth ; }
e ls e { return 0; }
}

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

10.1.3 Implementare un tipo interfaccia


Per indicare che una classe Il metodo average visto nel paragrafo precedente è in grado di elaborare oggetti di
implementa un'interfaccia si usa qualsiasi classe che implementi l’interfaccia Measurable. Una classe implementa o realizza
la parola riservata im p le m e n ts . un’interfaccia se la dichiara in una clausola implements, in questo modo;
public c la ss BankAccount implements Measurable

Di conseguenza, la classe deve implementare i metodi astratti richiesti dall’interfaccia:


public c la ss BankAccount implements Measurable
{
public double getMeasure()
{
return balance;
}
}

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:

Measurable obj = new BankAccount(); / / va bene

Una variabile di tipo Measurable può contenere un riferimento a un oggetto che sia
esemplare di una classe qualsiasi che implementa l’interfaccia Measurable.

Sintassi di Java 10.2 Implementazione di interfaccia



Sintassi public c la s s NomeClasse implements Nome In te rf ac ci al, No me In te rf ae ei al,
{
variabili di esemplare
metodi

Esempio Elenco di tu tte le interfacce


public c la ss BankAccount implements Measurable
realizzate dalla classe.

Variabili di esemplare public double getMeasure()


di B an kA cco u n t { Questo metodo fornisce !Implementazione
return balance;
del metodo dichiarato nellinterfaccia.
}
Altri metodi
di B an kA eeo u n t
518 C apitolo 10

Analogamente, è semplice modificare la classe Country perché implementi l’interfaccia


Measurable:

public c la ss Country implements Measurable


{
public double getMeasure()

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

i f (ob jects.len g th > 0) { return sum / o b jects.len gth ; }


e ls e { return 0; }

File MeasurableTester.java

Questo programma usa le c la s s i "misurabili" BankAccount e Country.


*/
public c la ss MeasurableTester
{
public s ta tic void main(String[] args)
{
/ / invocazione di average con un array di o g g e tti BankAccount
Measurable[] accounts = new Measurable[3];
accounts[o] = new BankAccount(O);
accounts[l] = new BankAccount(lOOOO);
accounts[2] = new BankAccount(2000);

double averageBalance = Data.average(accounts);


System .ou t.p rin tIn("Average balance: " + averageBalance);
System. ou t. p rintIn( "Expected : 4000") ;

/ / invocazione di average con un array di o g g e tti Country


Measurable[] countries = new Measurable[3];
countries[O] = new Country("Uruguay", 176220);
c o u n tr ies[l] = new Country("Thailand", 513120);
countries[2] = new Country("Belgium", 30510);

double averageArea = D ata.average(countries);


System.out.println("Average area: " + averageArea);
System. ou t. p r in tln ( "Expected : 239950") ;

Esecuzione del programma


Average balance: 4000.0
Expected: 4000
Average area: 239950.0
Expected: 239950

10 .1.4 Confronto tra ereditarietà e interfacce


Nel Capitolo 9 avete visto come usare l’ereditarietà per costruire un modello di
una gerarchia di classi correlate, come possono esserlo i diversi tipi di domande che
compongono un questionario: le domande con risposta a scelta multipla e le domande
con spazi da riempire sono due esempi del più generico concetto di “domanda”, ciascuno
con le proprie caratteristiche specifiche.
Le interfacce consentono di rappresentare una relazione un po’ diversa. Considerate,
ad esempio, le classi BankAccount e Country viste nel paragrafo precedente: entrambe
implementano l’interfaccia Measurable, ma non hanno altre caratteristiche in comune.
520 C apitolo 10

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

dove l’interfaccia Named è così definita:

public in terface Named


{
String getNameO;
}

Al contrario, una classe può estendere un’unica superclasse.


Un’interfaccia descrive il comportamento che deve essere esibito da chi la implementa
e prima della versione 8 di Java un’interfaccia non poteva definire alcuna implementazione.
Ora, invece, è possibile definire in un’interfaccia un’implementazione “predefinita” o di
default e, di conseguenza, la distinzione tra interfacce e classi astratte (viste nella sezione
Argomenti avanzati 9.3) è più sottile: una differenza rilevante che rimane è il fatto che
un tipo interfaccia, diversamente da una classe astratta, è privo di informazioni di stato, cioè
non ha variabili di esemplare.
In generale, si progetta un’interfaccia quando si scrive codice che elabori in modo
molto simile oggetti di classi diverse. Ad esempio, un programma di disegno potrebbe
dover disegnare oggetti graficamente diversi, come segmenti, immagini, testo e così via:
in tale situazione sarà utile un’interfaccia, ad esempio Drawable, che definisca il metodo
draw, da invocare per disegnare la forma. Un altro esempio può essere una simulazione di
traffico stradale, che consente il movimento di persone, automobili, cani, palloni e così
via: si potrebbe definire l’interfaccia Moveable, con i metodi move (per spostare un’entità)
e g e tP o sitio n (per ispezionarne la posizione).

.-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

Errori comuni 10.1


Dimenticarsi di dichiarare pubblici i metodi implementati
I metodi di un’interfaccia non vengono dichiarati public, perché lo sono per impostazione
predefinita.Tuttavia, i metodi di una classe non sono pubblici se ciò non viene specificato
esplicitamente, perché la loro modalità di accesso predefinita è quella “di pacchetto”, come
abbiamo visto nel Capitolo 8. Dimenticare la parola riservata public nella realizzazione
di un metodo relativo a un’interfaccia è un errore comune:
public c la ss BankAccount implements Measurable
{
double getMeasureO / / deve essere public
{
return balance;

Di conseguenza, il compilatore segnala che il metodo ha una modalità di accesso più


ristretta, “di pacchetto” anziché pubblica. La soluzione consiste nel dichiarare il metodo
con attributo public.

Errori comuni 10.2


Cercare dì creare un esemplare dì un tipo interfaccia
È possibile dichiarare una variabile il cui tipo sia un’interfaccia:

Measurable meas;

Ma non si può costruire un oggetto di un tipo interfaccia:

Measurable meas = new Measurable(); / / ERRORE

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:

Measurable meas = new BankAccount(); / / c o sì va bene

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.

NQtepfifiaya.8. lo.i ............................ ... .


Metodi statici nelle interfacce
Prima di Java 8 tutti i metodi delle interfacce dovevano essere astratti, mentre la versione
Java 8 consente di definire nelle interfacce metodi statici che funzionano esattamente
come i metodi statici definiti nelle classi. Un metodo statico definito in un’interfaccia
non opera su un oggetto e il suo scopo dovrebbe essere in qualche modo correlato a
quello dell’interfaccia in cui è contenuto.
Ad esempio, sarebbe decisamente sensato definire il metodo average visto nel Paragrafo
10.1 all’interno dell’interfaccia Measurable:

public in terface Measurable


{
double getMeasureO; / / un metodo a stra tto
s ta tic double average(Measurable[] ob jects) / / un metodo s ta tic
{
. . . / / la ste ssa implementazione v ista nel Paragrafo 10.1
}
}

Per invocare tale metodo si scrive il nome dell’interfaccia che lo contiene:

double meanArea = Measurable.average(countries);

NoteperJavaS 10.2__ ...


Metodi di default
Un m etodo predefinito o di default {default method) in un’interfaccia è un metodo
non statico di cui viene definita anche !’implementazione. Una classe che implementa
l’interfaccia erediterà il comportamento predefmito del metodo oppure lo potrà
sovrascrivere: il fatto che in un’interfaccia venga predefmita un’implementazione per
un metodo alleggerisce il lavoro necessario per realizzare una classe che la implementi.
Ad esempio, l’interfaccia Measurable potrebbe dichiarare il metodo getMeasure come
predefmito, in questo modo:
Interfacce 523

public in terface Measurable


{
default double getMeasure() { return 0; }
}

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:

public in terface Measurable


{
double getMeasureO; / / un metodo a stratto
default boolean smallerThan(Measurable other)
{
return getMeasure() < o th er.getMeasure();
}
}

Il metodo smallerThan verifica se un oggetto ha una misura inferiore a quella di un altro,


azione utile per disporre oggetti in ordine di misura crescente.
Una classe che voglia implementare l’interfaccia Measurable deve soltanto definire il
metodo getMeasure, ereditando automaticamente il metodo smallerThan: un meccanismo
che può essere molto utile. Ad esempio, l’interfaccia Comparator descritta nella sezione
Argomenti avanzati 13.5 ha un metodo astratto ma più di una dozzina di metodi predefiniti.

Note per Java 8 10.3 ....... ....................... ........ ......... ____


Conflitto tra metodi di default
Anche se è un caso piuttosto raro, è possibile che una classe erediti metodi predefiniti
da due interfacce diverse e che questi siano in conflitto tra loro, oppure che erediti un
metodo predefmito che sia in conflitto con uno dei propri metodi. Due regole servono
proprio a dirimere questi conflitti:

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.

Per comprendere appieno queste regole, analizziamo l’esempio che segue:

public c la s s Person
{
public String name() { return firstName() + " " + lastName(); }

} * *

public in terface Named


524 C apitolo 10

default String name() { return "(NONE)"; }


}
public c la ss User extends Person implements Named
{
/ / eredita name() da Person

In questo caso il metodo definito nella superclasse vince sul metodo definito nell’interfaccia.
Se, però, Person è un’interfaccia, la situazione è diversa:

public in terface Person


{
default String name() { return firstName() + " " + lastName(); }

} ’ * ’

Immaginiamo, ora, che una classe implementi entrambe le interfacce:


public c la ss User implements Person, Named { . . . }

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.

10.2.1 Conversione da classe a interfaccia


Guardate attentamente questa invocazione di metodo nel programma del paragrafo
precedente:

double averageBalance = Data.average(accounts);

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:

Measurable meas = new Rectangle(5, 10, 20, 30); / / ERRORE

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 "

10.2.2 Invocare metodi con variabili interfaccia


Supponiamo, ora, che la variabile meas sia stata inizializzata con un riferimento a un oggetto
che sia esemplare di una classe che implementa l’interfaccia Measurable. Non sappiamo
di quale classe l’oggetto sia esemplare, ma sappiamo che quella classe implementa tutti i
metodi dell’interfaccia, quindi li possiamo invocare:
doublé r esu lt = meas.getMeasure();

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

10.2.3 Conversione da interfaccia a classe


A volte può accadere di memorizzare un oggetto in una variabile riferimento di tipo
interfaccia e, poi, di avere la necessità di riconvertirlo al suo tipo originale. Analizziamo
questo metodo che restituisce l’oggetto avente la misura maggiore tra i due ricevuti
come parametri:
public static Measurable larger(Measurable objl, Measurable obj2)
{
if (obj.getMeasureO > obj2.getMeasure())
{
return objl;
}
else
{
return obj2;
}
}

Il metodo larger restituisce l’oggetto avente la misura maggiore sottoforma di riferimento di


tipo Measurable. Non c’è alternativa: il metodo non conosce il tipo effettivo degli oggetti
che ha ricevuto. Proviamo a usare il metodo:

Country Uruguay = new Country("Uruguay", 176220);


Country thailand = new Country("Thailand", 513120);
Measurable max = larger(uruguay, thailand);

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:

String countryName = max.getName(); / / ERRORE

Questa invocazione è un errore, perché il tipo Measurable non ha un metodo getName.


Tuttavia, essendo assolutamente certi che max si riferisca a un oggetto di tipo Country,
potete usare la notazione di forzatura (cast) per convertirlo al suo tipo originario:
Country maxCountry = (Country) max;
String name = maxCountry.getName();

Se vi siete sbagliati e l’oggetto in realtà non è una nazione, si verificherà un’eccezione


durante l’esecuzione.
Interfacce 527

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

Per poter gestire sequenze di qualunque tipo, dichiariamo un’interfaccia dotata di un


unico metodo:

public in terface Sequence


{
in t next();
}
528 C apitolo 10

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.

public void process(Sequence seq, in t valuesToProcess)


{
for (in t i = I; i <= valuesToProcess, i++)
{
in t value = seq .n ex t();
in t Ia stD ig it = value % 10;
counters[Ia s tD ig it]++;
}
}

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 c la ss SquareSequence implements Sequence


{
private in t n;

public in t next()
{
n++;
return n * n;
}
}

public c la s s RandomSequence implements Sequence


{
public in t next()
{
return (in t) ( In teg er.MAX_VALUE * Math.random());
}
}

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 ();

LastD igitD istribution d ist2 = new L astD igitD istribution();


d i s t 2 .process(new RandomSequence(), 1000);
Interfacce 529

d ist2 .d isp la y ();

Esecuzione del programma


IOO
200
O
O
200
100
200
O
O
200

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.

10.3 Unterfaiccìa Comparable


Implementate l'interfaccia Nei paragrafi precedenti abbiamo definito l’interfaccia Measurable e abbiamo progettato
Comparable in modo che gli oggetti il metodo average che funziona con qualsiasi classe che implementi tale interfaccia. In
delle vostre classi possano essere questo paragrafo conoscerete l’interfaccia Comparable della libreria standard di Java.
confrontati tra loro, ad esempio L’interfaccia Measurable è utile per misurare singoli oggetti, mentre l’interfaccia
in un metodo di ordinamento. Comparable è più complessa, dato che i confronti coinvolgono necessariamente due oggetti.
L’interfaccia dichiara soltanto il metodo compareTo e l’invocazione

a.compareTo(b)

deve restituire un numero negativo se a precede b, zero se a e b sono uguali e un numero


positivo se b precede a.
L’interfaccia Comparable ha un unico metodo:

public in terface Comparable


{
in t compareTo(0bject otherObject);
}
530 C apitolo 10

Ad esempio, la classe BankAccount può implementare Comparable in questo modo:

public c la ss BankAccount implements Comparable


{
public in t compareTo(Object otherObject)
{
BankAccount other = (BankAccount) otherObject;
i f (balance < other.balance) { return - I ; }
i f (balance > other.balance) { return I ; }
return 0;
}
}

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:

BankAccount other = (BankAccount) otherObject;

Dato che ora la classe BankAccount implementa l’interfaccia Comparable, si può ordinare un
array di conti bancari usando il metodo A rrays.sort:

BankAccount[] accounts = new BankAccount[3];


accounts[0] = new BankAccount(lOOOO);
accou nts[l] = new BankAccount2);
accounts[2] = new BankAccount(lOOOO);
Arrays. sort(accounts) ;

Ora !’array accounts è ordinato per saldo crescente.

.^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.

I Suggerimeatj peiJajjtoatammaziQne lQ.i .


Confrontare numeri interi e numeri in virgola mobile
Quando si scrive un metodo di confronto bisogna restituire un numero negativo per
segnalare che il primo oggetto precede il secondo, zero se i due oggetti sono uguali e un
Interfacce 531

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;

public c la ss Person implements Comparable


{
private in t id; / / deve essere >= 0

public in t compareTo(Object otherObject)


{
Person other = (Person) otherObject;
return id - oth er.id ;
}
}

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:

return In teg er.compare(id , o th e r .id ); / / anche con numeri negativi

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;

public c la s s BankAccount implements Comparable


{
public in t compareTo(Object otherObject)
{
BankAccount other = (BankAccount) otherObject;
return Double.compare(balance, other.balance);
}

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;

BankAccount account = new BankAccount(lOOOO);


BankAccount account2 = account;
account2 . d ep osit( 5000);
/ / ora sia account sia account2 puntano a un conto avente saldo 15000

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 =

Ecco come invocarlo:

BankAccount clonedAccount = (BankAccount) account.c lo n e();

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();
}

Purtroppo tutte queste protezioni implicano che i legittimi invocanti di O b je ct.clo n e ()


paghino un prezzo, ovvero che debbano intercettare l’eccezione (si veda il Capitolo 11)
anche se la loro classe implementa Cloneable.
534 C apitolo 10

public c la s s BankAccount implements Cloneable


{
public Object clon e()
{
try
{
return su p er.clon e();
}
catch (CloneNotSupportedException e)
{
/ / non può accadere perché implementiamo Cloneable,
/ / ma dobbiamo in te r c e tta r la comunque
return nu li;
}
}
}

Se un oggetto contiene un riferimento a un altro oggetto modificabile, dovete invocare


clone con tale riferimento. Per esempio, supponete che la classe Customer abbia una variabile
di esemplare di tipo BankAccount. In questo caso, potete realizzare Customer.clone nel modo
seguente:

public c la ss Customer implements Cloneable


{
private String name;
private BankAccount account;

public Object clon e()


{
try
{
Customer cloned = (Customer) su per.clon e();
cloned.account = (BankAccount) accou nt.clone();
return cloned;
}
catch (CloneNotSupportedException e)
{
/ / non può accadere perché implementiamo Cloneable
return nu ll;
}
}

In generale, !’implementazione del metodo clon e richiede queste fasi:

Dichiarare che la classe implementa l’interfaccia Cloneable.


Nel metodo clone, invocare su p e r .c lo n e (), catturando CloneNotSupportedException se la
superclasse è Object.
Clonare le variabili di esemplare che fanno riferimento a oggetti modificabili.
Interfacce 535

In questo paragrafo introdurremo il concetto di “smistamento” o “richiamata” (callback),


mostrando come possa rendere il metodo average ancora più flessibile, e vedremo come
si realizzi un tale smistamento in Java mediante le interfacce.
Per capire il motivo per cui vogliamo migliorare ancora il metodo average, considerate
queste importanti limitazioni dovute all’utilizzo dell’interfaccia Measurable.

• Potete aggiungere !’implementazione dell’interfaccia M easurable soltanto a classi


che sono sotto il vostro controllo. Se volete elaborare un insieme di oggetti di
tipo Rectangle, non potete fare in modo che la classe Rectangle implementi un’altra
interfaccia oltre a quelle che già realizza: è una classe di libreria, che non potete
modificare.
• Potete “misurare” un oggetto in un unico modo: se volete analizzare un insieme di
automobili sia in base alla velocità che in base al prezzo, siete bloccati.

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:

public in terface Measurer


{
double measure(Object anObject);
}

Il metodo measure misura un oggetto e restituisce il valore ottenuto mediante tale


misurazione. In questa definizione usiamo la proprietà, che hanno tutti gli oggetti, di
poter essere convertiti nel tipo Object.
Il codice che effettua l’invocazione del metodo di smistamento riceve un oggetto di
una classe che implementa questa interfaccia. Nel nostro caso, il metodo average migliorato
riceve un oggetto di tipo Measurer:
public s ta tic double average(Object[] o b jects, Measurer meas)
{
double sum = O;
for (Object obj : ob jects)
536 C apitolo 10

sum = sum + meas.measure(obj);


}
i f (ob jects.len gth > O) { return sum / o b jects.len gth ; }
e ls e { return 0; }
}

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:

Rectangle aRectangle = (Rectangle) anObject;

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);

Il metodo average chiederà all’oggetto AreaMeasurer di misurare i rettangoli.


La Figura 6 mostra il diagramma UML delle classi e delle interfacce usate in
questa soluzione. Come nella Figura 1, la classe Data (che contiene il metodo average)
è disaccoppiata dalla classe Rectangle, di cui elabora oggetti. Tuttavia, diversamente da
quanto accadeva nella Figura l,la classe Rectangle non è più accoppiata a un’altra classe:
per elaborare rettangoli, forniamo una piccola classe “ausiliaria”, AreaMeasurer, che ha
solamente lo scopo di aiutare il nietodo average a misurare gli oggetti.
Ecco il programma completo.
Interfacce 537

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

Descrive c la s s i i cui esemplari possono misurare a l t r i o g g e tti.


*/
public in terface Measurer
{
/**
Calcola la misura di un oggetto.
@param anObject !^oggetto da misurare
@return la misura
*/
double measure(Object anObject);
}

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

public s t a t ic double average(Object[] ob jects. Measurer meas)


{
double sum = 0;
for (Object obj : ob jects)
{
sum = sum + meas.measure(obj);
}
i f (o b jects.len g th > O) { return sum / o b jects.len g th ; }
e ls e { return O; }
}
}

File MeasurerTester.java
import java.aw t.R ectangle;

Questo programma illu s t r a ! ' u t i li z z o di un Measurer.


*/ "

public c la ss MeasurerTester
{
public s ta tic void m ain(String[] args)
{
Measurer areaMeas = new AreaMeasurer();

Rectangle[] rec ts = new Rectangle[]


{
new Rectangle(5, 10, 2 0 , 30),
new Rectangle(lO, 20, 30, 40),
new Rectangle(20, 30, 5, 15)
};
double averageArea = D ata.average(rects, areaMeas);
System.out.println("Average area: " + averageArea);
System. ou t. p rintIn( "Expected : 625");

Esecuzione del programma


Average area: 625.0
Expected: 625

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.

Note per Java 8 10.4 __________............................. ......... ...........................


Espressioni lambda
Nel paragrafo precedente avete visto come usare le interfacce per specificare varianti
nel comportamento: il metodo average ha bisogno di misurare qualsiasi oggetto e lo fa
invocando il metodo measure dell’oggetto Measurer che ha ricevuto.
Sfortunatamente, chi vuole invocare il metodo average ha molto lavoro da fare: deve
definire una classe che implementi l’interfaccia Measurer, per poi costruirne un esemplare.
La versione 8 di Java consente l’uso di una comoda scorciatoia, a patto che l’interfaccia
abbia un unico metodo astratto. Una tale interfaccia viene chiamata interfaccia funzionale,
perché il suo scopo è quello di definire una sola funzione, e l’interfaccia Measurer ne è un
esempio.
Per specificare tale unica funzione si può usare una espressione lambda, cioè
un’espressione che definisce in modo compatto i parametri ricevuti da un metodo e il
valore che questo restituisce. Ecco un esempio:

(Object obj) -> ((BankAccount) obj).getB alance()

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:

Measurer accountMeas = (Object obj) -> ((BankAccount) ob j).getB alan ce();

A questo punto accadono tre cose:

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

double averageBalance = average(accounts,


(Object obj) -> ((BankAccount) ob j).getB alan ce());

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:

public s ta tic double average(Object[] o b jects, Measurer meas)


{
sum = sum + meas.measure(obj);

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:

Measurer areaMeas = (Object obj) ->


{
Rectangle r = (Rectangle) obj;
return r.getW idth() * r.getH eigh t();
};

In linea teorica è forse più facile comprendere le espressioni lambda se le si immagina


come una notazione particolarmente comoda per realizzare il meccanismo di callback.
Prendiamo in esame un generico metodo che ha la necessità di invocare un frammento
di codice che varia da un’invocazione all’altra. Questo risultato si può ottenere in questo
modo:

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).

Argomenti avanzati .. . ........ .......... ........................


Tipi interfaccia generici
Nel Paragrafo 10.3 avete visto come utilizzare la versione “semplice” del tipo interfaccia
Comparable, ma, in
effetti, l’interfaccia Comparable è un tipo parametrico, come il tipo ArrayList:
Interfacce 541

public in terface Comparable<T>


{
in t compareTo(T other);
}

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:

public c la ss BankAccount implements Comparable<BankAccount>


{
public in t compareTo(BankAccount other)
{
return Double.compare(balance, other.balance);
}
}

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:

public in terface Measurer<T>


{
double measure(T anObject);
}

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:

public c la s s AreaMeasurer implements Measurer<Rectangle>


{
public double measure(Rectangle anObject)
{
return anObject.getWidth() * anO bject.getHeight();
}
}

10.5 Classi interne


La classe AreaMeasurer del paragrafo precedente è veramente banale: ne abbiamo bisogno
soltanto perché il metodo average richiede un oggetto di una classe che implementi
l’interfaccia Measurer. Quando avete una classe che serve a uno scopo molto limitato, come
questo, potete dichiararla all’interno del metodo che ne ha bisogno:
public c la ss MeasurerTester
{
public s ta tic void m ain(String[] args)
{
c la s s AreaMeasurer implements Measurer
542 C apitolo 10

Measurer areaMes = new AreaMeasurer();


double averageArea = D ata.average(rects, areaMes);

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
{
} ’ *

public s ta tic void main(String[] args)


{
Measurer areaMes = new AreaMeasurer();
double averageArea = D ata.average(rects, areaMes);

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

Argomenti avanzati 10,4. _____


Classi anonime
U n’entità è anonima quando non possiede un nome. In un programma, qualcosa che si
usa una volta sola generalmente non ha bisogno di un nome. Per esempio, se la nazione
memorizzata nella variabile belgium non viene usata in altre zone del metodo, potete
sostituire questi enunciati:

Country belgium = new Country("Belgium", 30510);


co u n tries. add( belgium);

con il seguente:

countries.add(new Country("Belgium", 30510));

L’oggetto new Country("Belgium", 30510) è un oggetto anonimo. Ai programmatori


piacciono gli oggetti anonimi, perché non devono sforzarsi di trovare un nome per la
variabile che ne memorizza il riferimento: se avete mai dovuto affrontare una decisione
sofferta per capire se il riferimento a una moneta dovesse chiamarsi c, dime oppure aCoin,
potete comprendere questo atteggiamento.
Spesso con le classi interne ci troviamo in una situazione simile: dopo aver costruito
un unico oggetto di tipo AreaMeasurer, tale classe non viene più usata. In Java, è possibile
dichiarare classi anonime, se tutto ciò di cui avete bisogno è un singolo esemplare della
classe.
public s ta tic void m ain(String[] args)
{
/ / costruzione di un oggetto di una c la sse anonima
Measurer m = new Measurer()
/ / la dichiarazione d ella c la sse in iz ia qui
{
public double measure(0bject anObject)
{
Rectangle aRectangle = (Rectangle) anObject;
return aRectangle.getW idth() * aR ectangle.getH eight();
}
};
double r esu lt = D ata.average(rectangles, m);

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:

public void addScore(int studentld, double score)


public double getAverageScore(int studentld)
public void save(String filename)

Consideriamo, ora, la classe GradingProgram, che elabora oggetti di tipo GradeBook,


invocandone metodi: vorremmo poterla collaudare prima di avere una classe GradeBook
pienamente funzionante.
Per poterlo fare dichiariamo un’interfaccia avente gli stessi metodi della classe GradeBook;
spesso, per convenzione, si usa la lettera I maiuscola come prefisso per il nome di tale
interfaccia.

public in terface !GradeBook


{
void addScore(int studentld, double score);
double getAverageScore(int studentld);
void save(String filename);

} * * *

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 c la ss MockGradeBook implements !GradeBook


{
private ArrayList<Double> scores;

public void addScore(int studentld, double score)


{
/ / ignora studentld
sc o r e s.add(score);
}
public double getAverageScore(int studentld)
{
double to ta l = 0;
for (double x : scores) { to ta l = to ta l + x; }
return to ta l / s c o r e s.s iz e ();
Interfacce 545

}
public void save(String filename)
{
/ / non fa nulla

A questo punto possiamo costruire un esemplare di MockGradeBook e usarlo nella classe


CradingProgram, riuscendo così a collaudarla immediatamente. Quando, poi, saremo pronti
per collaudare la vera classe GradeBook, useremo un suo esemplare. Evitate, comunque, di
cancellare la classe semplificata: sarà utile per il collaudo regressivo.

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:

public in terface ActionListener


{
void actionPerformed(ActionEvent event);
}

Questa particolare interfaccia ha un unico metodo, actionPerformed. Il vostro compito


consiste nel progettare una classe il cui metodo actionPerformed contenga le istruzioni che
volete che vengano eseguite ogni volta che viene premuto il pulsante.
Interfacce 547

Figura 7 •iT w m In a l J-Ialx


f ile £ d it y ie w J e rm in a l Tafas H e lp
P ro g ra m m a co n
~ $ CdBigJava/chl0/buttonl !
u n ric e v ito re di a z io n i
~/BigJava/chl0/buttonl$ javac ButtonViewer .java
~/BigJava/chl0/buttonl$ java ButtonViewer
I was clicked.
I was clicked.
I was clicked ih \ I-IalX I
I
C lick m el
]

Ecco un esempio molto semplice di una classe di questo tipo.

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");
}
}

Abbiamo ignorato la variabile parametro event del metodo actionPerformed: contiene


ulteriori dettagli relativi all’evento, come l’istante in cui esso è avvenuto.
Usate componenti di tipo J B u tto n Dopo aver dichiarato la classe per il ricevitore di eventi, dobbiamo costruirne un
per realizzare pulsanti grafici esemplare e associarlo al pulsante grafico:
e associate un A c t io n L is t e n e r
ActionListener lis te n e r = new C lick L isten er();
a ogni pulsante.
button. addActionListener(lis t e n e r ) ;

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) ;

e, di conseguenza, viene visualizzato il messaggio.


Potete pensare al metodo actionPerformed come a un ulteriore esempio di callback,
simile al metodo measure dell’interfaccia Measurer. Il gestore Java dell’ambiente grafico
invoca il metodo actionPerformed ogni volta che viene premuto il pulsante, così come il
metodo D ata.average invoca il metodo measure ogni volta che ha bisogno di misurare un
oggetto.
548 C apitolo 10

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;

Questo programma mostra eome s i in s ta lla un r ie e v ito r e di azio n i.


*/ '
publie e la s s ButtonViewer
{
private s ta tie final in t FRAME_WIDTH = 100;
private s ta tie final in t FRAME_HEICHT = 60;

publie s ta tie void m ain(String[] args)


{
!Frame frame = new !Frame();
!Button button = new !Button(''Cliek me!");
frame.add(button);

AetionListener lis te n e r = new C liek L isten er();


button. addAetionListener(lis t e n e r ) ;

frame. setSize(FRAME_WIDTH, FRAME_HEIGHT);


frame. setDefaultCloseOperation( ! Frame. EXIT_0N_CLOSE) ;
fram e.setV isib le(tru e);

10.7.2 Classi interne come ricevitori di eventi


Nel paragrafo precedente avete visto che il codice che deve essere eseguito in risposta
alla selezione di un pulsante grafico viene inserito in una classe che funge da ricevitore
di eventi. Una classe di questo tipo viene molto spesso realizzata come classe interna, in
questo modo:
!Button button = new ! Button( " . .." );

/ / questa c la sse interna viene definita nel metodo


/ / in cui s i trova la v a ria b ile che contiene i l pulsante
c la ss MyListener implements ActionListener
{

} ’ ’ ’

ActionListener lis te n e r = new MyListener();


button. addAetionListener( lis t e n e r ) ;
Interfacce 549

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);

/ / questa c la sse interna viene dichiarata a ll'in te r n o del


/ / metodo in cui s i trovano le v a r ia b ili account e button
c la ss AddInterestListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
/ / i l metodo del r ic ev ito r e accede a lla v a ria b ile
/ / account v i s i b i le nel blocco circostan te
double in te r e st = account.getBalance() * INTEREST_RATE / 100;
account. d ep osit( in te r e s t);
}
}
ActionListener lis te n e r = new AddInterestListener();
button. addActionListener( lis t e n e r ) ;

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.

File InvestmentViewerI .java


import java. awt. even t.ActionEvent;
import java. awt. even t.A ctionL istener;
import javax.sw ing.DButton;
import javax.swing.DFrame;
:>^u LA P IT O LO IU

/**
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;

private s t a t ic final double INTEREST_RATE = 10;


private s t a t ic final double INITIAL_BALANCE = 1000;

public s ta tic void m ain(String[] args)


{
!Frame frame = new !Frame();

/ / i l pulsante che fa partire l'elaborazione


!Button button = new !Button("Add In terest" );
frame.add(button);

/ / l'ap p licazion e accredita g l i in te r e s s i a questo conto bancario


final BankAccount account = new BankAccount(INITIAL_BALANCE);

c la ss AddInterestListener implements ActionListener


{
public void actionPerformed(ActionEvent event)
{
/ / i l metodo del r ic e v ito r e accede a lla v a riab ile
/ / account del blocco circostan te
double in te r e st = account.getBalance() * INTEREST_RATE / 100;
account. d ep o sit( in te r e s t);
System.o u t.println("balance: " + account.getB alance());
}
}
ActionListener lis te n e r = new A ddInterestListener();
button. addActionListener( lis t e n e r ) ;

frame. setSize(FRAME_WIDTH, FRAME_HEIGHT);


frame. setDefaultCloseOperation ( ! Frame. EXIT_ON_CLOSE);
frame.s e tV isib le (tr u e );

Esecuzione del programma


balance: llOO.O
balance: 1210.0
balance: 1331.0
balance: 1464.1
26. Nel programma ButtonViewer, quali sono gli oggetti che rappresentano la sorgente di
eventi e il ricevitore di eventi?
27. Perché è lecito assegnare un oggetto di tipo C lic k L is te n e r a una variabile di tipo
A ctionListener?
28. In quali situazioni il metodo aetionPerformed deve essere invocato dal programmatore?
29. Perché a volte un metodo di una classe interna ha bisogno di accedere a una variabile
che si trova in un ambito di visibilità circostante?
30. Se una classe interna accede a una variabile locale che si trova in un ambito di visibilità
circostante, quale speciale regola occorre applicare?

Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R 10.16, R 10.22 e E l0.17, al termine
del capitolo.

. . ........ ................ .. ..............

Implementare un metodo modificando il tipo dei suoi parametri


Quando implementate un’interfaccia dovete dichiarare ciascun metodo esattamente come
specificato nell’interfaccia stessa: fare piccole modifiche accidentali ai tipi dei parametri
è un errore piuttosto frequente. Ecco un classico esempio:
c la ss MyListener implements ActionListener
{
public void aetionPerformed0
/ / ah i, abbiamo dimenticato i l parametro ActionEvent
{

}
}

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.

JB EiTorLcQimiDLlOA ....... . ............. . ......


Tentare di invocare i metodi dei ricevitori
Alcuni studenti cercano di scrivere codice che invoca esplicitamente i metodi dei ricevitori
di eventi:

ActionEvent event = new ActionEvent(. . . ) l H fa te lo !


lis t e n e r . a c tionPerformed(event) ;
552 C apitolo 10

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).

NoteperJavaS 10.4 ........ .... .., ™ ..... . .„ .


Le espressioni lambda per la gestione degli eventi
La sezione Note per Java 8 10.4 ha illustrato l’uso delle espressioni lambda come esemplari
di classi che implementano un’interfaccia funzionale, cioè un’interfaccia dotata di un
unico metodo astratto. Tra queste interfacce figurano certamente i gestori di eventi,
come A ctionL istener.
Ad esempio, invece di definire la classe C lick L isten er, per poi associarne un esemplare
a un pulsante grafico, gli si può semplicemente associare il ricevitore, in questo modo:

button. addAetionListener(
(AetionEvent event) -> System.o u t.p r in tIn("I was e lie k e d ." ));

I M Jiostruire applicazioni dotate di pulsanti______


In questo paragrafo vedrete come si delinea un’applicazione grafica dotata di pulsanti.
Aggiungeremo un pulsante al nostro semplice programma che visualizza investimenti: ogni
volta che viene premuto il pulsante vengono accreditati sul conto bancario gli interessi
maturati e viene visualizzato il saldo aggiornato, come si può vedere nella Figura 8.

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");

Abbiamo anche bisogno di un componente dell’interfaccia grafica che visualizzi un


messaggio: il saldo attuale del conto bancario. Un componente di questo tipo è un etichetta
(label) e al costruttore di DLabel viene fornita la stringa contenente il messaggio che deve
essere visualizzato inizialmente, in questo modo:

DLabel la b el = new DLabel("balance = " + account.getB alance());

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

aggiungere al frame proprio il pannello, (dopo aver aggiunto a quest’ultimo i due


componenti:

DPanel panel = new DPanel();


panel.add(button);
pan el.add(label);
frame.add(panel);

!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());
}
}

Dobbiamo fare attenzione a un piccolo dettaglio tecnico: il metodo actionPerformed


usa le variabili account e la b e l, che sono variabili locali del metodo main del programma
di visualizzazione di investimenti bancari, non variabili di esemplare della classe
A ddInterestL istener. Quindi, se usiamo una versione di Java precedente alla versione 8,
dobbiamo dichiarare final le variabili account e la b e l, in modo che il metodo actionPerformed
vi possa accedere.
Mettiamo ora insieme la varie parti:
public s ta tic void m ain(String[] args)
{
DButton button = new DButton("Add In terest" );
final BankAccount account = new BankAccount(INITIAL_BALANCE);
final DLabel lab el = new DLabel("balance = " + account.getB alance());

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());
}
}
ActionListener lis te n e r = new AddInterestListener();
button. addActionListener(lis t e n e r ) ;
554 C apitolo 10

C o n u n p o ’ di e sp e rie n z a im p a re re te a le g g ere c o d ic e di q u e s to tip o c o m e se fosse u n testo:


“ Q u a n d o v ie n e p re m u to il p u lsa n te , a g g iu n g i gli in teressi e im p o sta il te sto d e ll’e tic h e tta ” .
E c c o il p ro g ra m m a c o m p le to , c h e m o stra c o m e si a g g iu n g o n o p iù c o m p o n e n ti a u n
fram e, u sa n d o u n p a n n e llo , e c o m e si p ro g e tta n o r ic e v ito ri d i e v e n ti so tto fo rm a di classi
in te rn e .

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;

Questo programma visualizza la ereseita di un investimento.


*/
publie elass InvestmentViewer2
{
private statie final int FRAME_W1DTH = 400;
private statie final int FRAME_HEIGHT = 100;

private statie final double INTEREST_RATE = 10;


private statie final double INITIAL_BALANCE = 1000;

publie statie void main(String[] args)


{
!Frame frame = new !Frame();

// il pulsante ehe fa partire !^elaborazione


!Button button = new !Button("Add Interest");

// I^applieazione aeeredita gli interessi su questo eonto baneario


final BankAeeount aeeount = new BankAeeount(INITIAL_BALANCE);

// Eetiehetta ehe visualizza i risultati


final !Label label = new !Label("balanee = " + aeeount.getBalanee());

// il pannello ehe eontiene i eomponenti dell'interfaeeia grafiea


!Panel panel = new !Panel();
panel.add(button);
panel.add(label);
frame.add(panel);

elass AddInterestListener implements AetionListener


{
publie void aetionPerformed(AetionEvent event)
{
double interest = aeeount.getBalance() ♦ INTEREST_RATE / 100;
aeeount.deposit(interest);
label. setText("balanee = ” + aeeount.getBalaneeO);
}
}
I nterfacce 555

ActionListener listener = new AddInterestListener();


button.addActionListener(listener);

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 .

Errori comuni 1 0 .5 ........................... ............... ...... .... ............... „ ......


Dimenticarsi di assodare un ricevitore
Se, e s e g u e n d o il v o stro p ro g ra m m a , sc o p rite c h e i p u lsa n ti s e m b ra n o n o n fu n z io n a re ,
c o n tro lla te b e n e di aver asso cia to il ric e v ito re p e r gli e v e n ti d ei p u lsa n ti; lo stesso vale
p e r gli altri c o m p o n e n ti d e ll’in te rfa c c ia u te n te . E u n e rro re c h e ca p ita fin tr o p p o spesso:
p ro g e tta re la classe ric e v ito re e la relativa a z io n e d i g e s tio n e d eg li ev e n ti senza asso cia rn e
u n esem p la re alla s o rg e n te d e ll’e v e n to stesso.

iiiflflfirim ftDlijierh . _________________


Non usate un contenitore come ricevitore di eventi
In q u e s to lib ro u sia m o classi in te r n e p e r i ric e v ito ri di ev e n ti deH’in te rfa cc ia grafica: q u esta
s o lu z io n e fu n z io n a c o n m o lti diversi tip i di e v e n ti, e, u n a v o lta p a d ro n e g g ia ta la te c n ic a ,
n o n d o v re te p en sarci p iù . In o ltre , m o lti a m b ie n ti di sv ilu p p o g e n e ra n o a u to m a tic a m e n te
c o d ic e s o rg e n te p e r l’in te rfa c c ia u te n te u sa n d o classi in te r n e , p e r c u i è b e n e a c q u isire
fam iliarità c o n esse.
T u tta v ia, m o lti p r o g ra m m a to ri e v ita n o di u sare classi p e r r ic e v ito ri di e v e n ti e, in vece,
tra s fo rm a n o u n c o n te n ito re (c o m e u n p a n n e llo o u n fram e) in u n ric e v ito re . E c c o u n
e s e m p io tip ic o , in cu i il m e to d o actionPerformed è stato a g g iu n to alla classe c h e si o c c u p a
della v isu a liz za zio n e , c io è il v isu a liz z a to re stesso im p le m e n ta l’in te rfa c c ia ActionListener:

public class InvestmentViewer


implements ActionListener // questo approccio è sconsigliato
{
public InvestmentViewerO
{
DButton button = new DButton("Add Interest");
button.addActionListener(this);
556 C apitolo 10

public void actionPerformed(ActionEvent event)


{

O ra il m e to d o actionPerformed fa p a rte della classe InvestmentViewer, a n z ic h é trovarsi in u n a


classe ric e v ito re sep arata, e il ric e v ito re v ie n e in stallato u sa n d o this.
Q u e s ta te c n ic a h a d u e p r o b le m i rile v a n ti. P rim a di tu tto , se p ara la d ic h ia ra z io n e
d e i p u ls a n ti d alle a z io n i re la tiv e ai p u lsa n ti stessi, c h e , n e l c o d ic e , r is u lta n o d ista n ti.
S e c o n d a r ia m e n te , n o n è f a c ilm e n te sc alab ile: se la classe d i v isu a liz z a z io n e h a d u e
p u ls a n ti, c ia s c u n o d ei q u a li g e n e ra e v e n ti, il m e to d o actionPerformed d e v e s c o p rire la
s o r g e n te d i o g n i e v e n to , u sa n d o c o d ic e n o io s o e in tr o d u c e n d o , q u in d i, u n a n u o v a fo n te
di e r r o ri.

10.9 Eventi di temporizzazione


In q u e s to p a ra g ra fo s tu d ie re m o gli e v e n ti di te m p o riz z a z io n e e m o s tre re m o c o m e essi
c o n s e n ta n o di realizzare se m p lic i a n im a z io n i.
La classe Timer d el p a c c h e tto javax.sw ing g e n e ra u n a se q u e n z a di e v e n ti, sep arati da
in te rv a lli di te m p o tu tti u g u a li fra lo ro (p o te te im m a g in a re u n te m p o riz z a to re c o m e u n
p u lsa n te in v isib ile c h e v ie n e p r e m u to a u to m a tic a m e n te e p e rio d ic a m e n te ). C iò è u tile
o g n i v o lta c h e v o le te d is p o rre di o g g e tti a g g io rn a ti a in te rv a lli re g o la ri: ad ese m p io , in
u n ’a n im a z io n e p o tre s te v o le r a g g io rn a re u n a scen a d ie c i v o lte al se c o n d o , rid is e g n a n d o
l’im m a g in e p e r d are l’illu sio n e d el m o v im e n to .
Un temporizzatore genera eventi Q u a n d o u sa te u n te m p o r iz z a to r e d o v e te f o rn ire al su o c o s tru tto r e la fre q u e n z a
a intervalli di tempo fissi. d eg li e v e n ti e u n o g g e tto di u n a classe c h e im p le m e n ti l’in te rfa c c ia ActionListener e c h e
c o n te n g a l’a z io n e d e sid e ra ta all’in te r n o d el p r o p rio m e to d o actionPerformed. In fin e , fate
p a rtire il te m p o riz z a to re .

class MyListener implements ActionListener


{
public void actionPerformed(ActionEvent event)
{
// azione che verrà eseguita a ogni evento di temporizzazione

}
}

MyListener listener = new MyListener();


Timer t = new Timer(interval, listener);
t.startO;

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

11 n o stro p ro g ra m m a e se m p lific a tiv o m o stre rà u n re tta n g o lo in m o v im e n to . P er p rim a


cosa ci serve u n a classe, RectangleComponent, il c u i m e to d o moveRectangleBy sposta il re tta n g o lo
d ella q u a n tità sp ecificata.

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 class RectangleComponent extends DComponent


{
private static final int BOX_X = 100;
private static final int BOX_Y = 100;
private static final int B0X_WIDTH = 20;
private static final int B0X_HEIGHT = 30;

private Rectangle box;

public RectangleComponentO
{
// il rettangolo che viene disegnato dal metodo paintComponent
box = new Rectangle(BOX_X, BOX_Y, BOX_WIDTH, BOX_HEICHT);
}

public void paintComponent(Graphics g)


{
Graphics2D g2 = (Graphics2D) g;
g2.draw(box);
}

/**
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;
}
}

Il metodo r e p a in t chiede I N o ta te l’in v o c a z io n e di repaint n e l m e to d o moveRectangleBy: è necessaria p e r essere c e rti c h e


a un componente di ridisegnare i il c o m p o n e n te v en g a rid is e g n a to d o p o aver m o d ific a to lo sta to d e l re tta n g o lo . R ic o r d a te
se stesso: invocatelo ogni volta se m p re c h e l’o g g e tto c h e ra p p re se n ta il c o m p o n e n te n o n c o n tie n e i p ix el c h e v e n g o n o
che modificate le forme grafiche ] v isu a liz za ti, m a c o n tie n e s o la m e n te u n o g g e tto di tip o Rectangle, il q u a le a sua v o lta
Ilvengonodisegnatedalmetodo 1 c o n tie n e q u a ttro n u m e ri. L’in v o c a z io n e di translate a g g io rn a i v a lo ri d elle c o o rd in a te del
p a in tC o m p o n e n t. i re tta n g o lo , m e n tr e l’in v o c a z io n e di repaint p ro v o c a , a sua v o lta , l ’in v o c a z io n e d el m e to d o
558 C apitolo 10

paintComponent, c h e , in fin e , rid ise g n a il c o m p o n e n te , fa c e n d o in m o d o c h e il re tta n g o lo


ap p aia n ella p o siz io n e a g g io rn a ta .
Il m e to d o actionPerformed d el ric e v ito re di ev e n ti d i te m p o riz z a z io n e h a il se m p lic e
c o m p ito di in v o c are component.moveRectangleBy(i, l): c iò sp o sta il re tta n g o lo di u n pix el
v erso il basso e v erso d estra. D a l m o m e n to c h e il m e to d o actionPerformed v ie n e in v o c a to
d ie c i v o lte al se c o n d o , il m o v im e n to del re tta n g o lo all’in te r n o del fra in e a p p a re flu id o .

File RectangleFrame.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DFrame;
import javax.swing.Timer;

Questo frame contiene un rettangolo che si sposta.


*/
public class RectangleFrame extends !Frame
{
private static final int FRAME_WIDTH = 300;
private static final int FRAME_HEIGHT = 400;

private RectangleComponent scene;

class TimerListener implements ActionListener


{
public void actionPerformed(ActionEvent event)
{
scene.moveRectangleBy(l, l);
}
}

public RectangleFrameO
{
scene = new RectangleComponent();
add(scene);

setSize(FRAME_WIDTH, FRAME_HEIGHT);

ActionListener listener = new TimerListener();

final int DELAY = 100; // millisecondi tra due eventi


Timer t = new Timer(DELAY, listener);
t.startO;

File RectangleViewer.java
import javax.swing.!Frame;

/**
Questo programma visualizza un rettangolo in movimento.
*/
I nterfacce 559

public class RectangleViewer


{
public static void main(String[] args)
{
!Frame frame = new RectangleFrame();
frame.setTitleC'An animated rectangle");
frame.setDefaultClose0peration(3Frame.EXIT_0N_CL0SE);
frame.setVisible(true);
}
}

33. P erché u n te m p o riz z a to re ha b iso g n o di u n rice v ito re di eventi?


34. C osa su c c e d e re b b e se nel m e to d o moveRectangleBy m ancasse l’in v o cazio n e del m e to d o
repaint?

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 .

irroiiiamunlJOi^,^.^. _ , ....... .......... . ... . .


Dimenticarsi dì rìdìsegnare
Q u a n d o i v o stri g esto ri di ev en ti m o d ific a n o i dati di u n c o m p o n e n te disegnato, o c c o rre fare
a tte n z io n e . D o p o c h e av ete m o d ific a to i d ati, il c o m p o n e n te n o n v ie n e a u to m a tic a m e n te
rid is e g n a to in base a tali n u o v i dati: o c c o rre in v o c a re il m e to d o repaint d el c o m p o n e n te
all’in te r n o d el g e sto re di ev e n ti o n e i m e to d i m o d ific a to ri d el c o m p o n e n te stesso. In
c o n s e g u e n z a di ciò , al m o m e n to o p p o r tu n o v e rrà in v o c a to il m e to d o paintComponent del
c o m p o n e n te , c h e ric e v e rà u n o p p o r tu n o o g g e tto d i tip o Graphics. N o ta te , p e rò , c h e n o n
d o v re ste m ai in v o c a re d ire tta m e n te il m e to d o paintComponent.
Q u e s to p r o b le m a r ig u a r d a s o la m e n te i c o m p o n e n ti d is e g n a ti da v o i. Q u a n d o
m o d ific a te le p r o p rie tà d i u n o d e i c o m p o n e n ti sta n d a rd di S w in g , c o m e D Label, il
c o m p o n e n te v ie n e rid ise g n a to a u to m a tic a m e n te .

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:

public interface MouseListener


{
void mousePressed(MouseEvent event);
// Chiamato quando un pulsante del mouse
// è stato premuto su un componente
void mouseReleased(MouseEvent event);
560 C apitolo 10

// Chiamato quando un pulsante del mouse


// è stato rilasciato su un componente
void mouseClicked(MouseEvent event);
// Chiamato quando un pulsante del mouse
// è stato premuto e rilasciato in rapida
// successione su un componente ("click")
void mouseEntered(MouseEvent event);
// Chiamato quando il mouse entra in un componente
void mouseExited(MouseEvent event);
// Chiamato quando il mouse esce da un componente
}

I m e to d i mousePressed e mouseReleased v e n g o n o in v o cati o g n i vo lta c h e u n p u lsa n te del m o u se


v ie n e , ris p e ttiv a m e n te , p r e m u to o rilasciato . Se u n p u lsa n te v ie n e p r e m u to e rila scia to in
rap id a su ccessio n e, senza c h e il m o u s e si sia sp o stato , allo ra v ie n e in v o c a to a n c h e il m e to d o
mouseClicked. I m e to d i mouseEntered e mouseExited p o sso n o essere u tiliz z a ti p e r visu alizzare
in m o d o sp e cia le u n c o m p o n e n te d e ll’in te rfa c c ia u te n te q u a n d o il p u n ta to r e d el m o u se
si tro v a al su o in te rn o .
11 m e to d o u sa to p iù f r e q u e n te m e n te è mousePressed: so lita m e n te gli u te n ti si a sp e tta n o
c h e le p r o p rie a z io n i v e n g a n o e la b o ra te n o n a p p e n a il p u lsa n te d el m o u s e v ie n e p re m u to .
P er a g g iu n g e re a u n c o m p o n e n te u n ric e v ito re di ev e n ti d el m o u se si in v o ca il m e to d o
addMouseListener:

public class MyMouseListener implements MouseListener


{
// implementazione dei cinque metodi
}

MouseListener listener = new MyMouseListener()


component.addMouseListener(listener);

N e l n o stro n u o v o p ro g ra m m a di ese m p io , o g n i v o lta c h e l’u te n te p re m e e rilascia il


p u lsa n te del m o u s e (c io è fa “ c lic k ” ) sulla fin estra , v o g lia m o c h e il r e tta n g o lo si sposti
e si p o siz io n i n e l p u n to in c u i si trova il m o u se . P er p r im a cosa m o d ific h ia m o la classe
RectangleComponent, a g g iu n g e n d o le u n m e to d o moveRectangleTo c h e sp o sti il r e tta n g o lo in
u n a n u o v a p o siz io n e .

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

private static final int BOX_HEIGHT = 30;

private Rectangle box;

public RectangleComponent2()
{
// il rettangolo che viene disegnato dal metodo paintComponent
box = new Rectangle(BOX_X, B0X_Y, B0X_WIDTH, BOX_HEICHT);
}

public void paintComponent(Graphics g)


{
Graphics2D g2 = (Graphics2D) g;
g2.draw(box);
}

Sposta il rettangolo alla posizione specificata.


@param x la coordinata x della nuova posizione
@param y la coordinata y della nuova posizione
*/
public void moveRectangleTo(int x, int y)
{
box.setLocation(x, y);
repaintO;
}
}

N o ta te T in v o c a z io n e d i repaint n el m e to d o moveRectangleTo: c o m e a b b ia m o v isto n el


p ara g ra fo p re c e d e n te , è n ec essaria p e r essere c e rti ch e , d o p o aver m o d ific a to lo sta to d el
re tta n g o lo , il c o m p o n e n te rid ise g n i se stesso, v isu a liz z a n d o il r e tta n g o lo nella sua n u o v a
p o siz io n e .
A q u e s to p u n to a g g iu n g ia m o al c o m p o n e n te u n ric e v ito re di e v e n ti d el m o u se : o g n i
v olta c h e v ie n e p r e m u to u n p u lsa n te d el m o u se , il ric e v ito re sp o sta il r e tta n g o lo nella
p o siz io n e in c u i si trova m o u se .

class MousePressListener implements MouseListener


{
public void mousePressed(MouseEvent event)
{
int X = event.getX();
int y = event.getY();
component.moveRectangleTo(x, y);
}

// metodi che non fanno niente


public void mouseReleased(MouseEvent event) {}
public void mouseClicked(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}

A ccad e spesso c h e u n p artico la re ric e v ito re di e v e n ti specifichi u n ’a z io n e in c o rrisp o n d e n z a


di u n o O d u e m e to d i so lta n to , a n c h e se i c in q u e m e to d i d e ll’in te rfa c c ia v a n n o c o m u n q u e
562 C apitolo 10

realizzati tu tti: i m e to d i in u tiliz z a ti v e n g o n o s e m p lic e m e n te realizzati s o tto fo rm a di


m e to d i c h e n o n fa n n o nulla.
O ra p ro c e d e te e d e s e g u ite il p ro g ra m m a RectangleViewerZ: o g n i v o lta c h e fate “ c lic k ”
c o n il m o u s e all’in te r n o d el fra m e, l’a n g o lo s u p e rio re sin istro del re tta n g o lo si sp o sta e
si p o siz io n a n el p u n to in c u i si trova il p u n ta to r e d el m o u se , c o m e si p u ò v e d e re n ella
F ig u ra 9.

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;

private RectangleComponentZ scene;

class MousePressListener implements MouseListener


{
public void mousePressed(MouseEvent event)
{
int X = event.getX();
int y = event.getY();
scene.moveRectangleTo(x, y);
}

// metodi che non fanno niente


public void mouseReleased(MouseEvent event) {}
public void mouseClicked(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
I nterfacce 563

public RectangleFrame2()
{
scene = new RectangleCofnponent2();
add(scene);

MouseListener listener = new MousePressListener();


scene.addMouseListener(listener);

setSize(FRAME_WIDTH, FRAME_HEIGHT);

File RectangleViewer2.java
import javax.swing.!Frame;

Visualizza un rettangolo che viene spostato dal mouse.


*/
public class RectangleViewer2
{
public static void main(String[] args)
{
!Frame frame = new RectangleFrame2();
frame.setDefaultCloseOperation(!Frame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

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?

Per far pratica


A q u e s to p u n to si co n sig lia di svolgere gli esercizi R IO . 19 e E 1 0 .2 9 ,a l te rm in e d el c a p ito lo .

A rgom ejiti avanzati 10.5


Eventi della tastiera
Se sc riv e te il p ro g ra m m a p e r u n g io c o al c a lc o la to re , p r o b a b ilm e n te v o rre te e la b o ra re
a n c h e gli e v e n ti della tastiera, ad e s e m p io p e r g estire le fre cc e d ire z io n a li: basta a g g iu n g e re
al c o m p o n e n te c h e u sa te p e r d ise g n a re la scen a di g io c o u n ric e v ito re d i e v e n ti della
tastie ra {key fetener). L’in te rfa c c ia KeyListener h a tre m e to d i m a ,c o m e n el caso d eg li e v e n ti
d el m o u se , sarete p rin c ip a lm e n te in te ressa ti agli e v e n ti c h e v e n g o n o p o sti in re la z io n e
c o n la p re ssio n e di u n ta sto (keyPressed) e p o tr e te lasciare v u o ti gli altri d u e m e to d i. U n a
classe usata c o m e ric e v ito re di e v e n ti della tastiera p o tr e b b e essere c o m e q u esta:
564 C apitolo 10

class MyKeyListener implements KeyListener


{
public void keyPressed(KeyEvent event)
{
String key = Keystroke.getKeyStrokeForEvent(event).toStringO;
key = key.replace("pressed ",
// elabora il tasto premuto

} ’ ' *

// metodi che non fanno niente


public void keyReleased(KeyEvent event) {}
public void keyTyped(KeyEvent event) {}
}

L’in v o c a z io n e Keystroke.getKeyStrokeForEvent(event).toString() tra sfo rm a l’o g g e tto e v e n to


in u n a d e s c riz io n e te stu a le d el tasto p re m u to , ad e s e m p io "pressed LEFT" se è sta to p r e m u to
il tasto d ella freccia v erso sin istra. N e lla se c o n d a rig a di c o d ic e d el m e to d o keyPressed
a b b ia m o e lim in a to il p refisso "pressed ": la p a rte rim a n e n te è u n a strin g a c o m e "LEFT"
o p p u re "A" e d e sc riv e il tasto c h e è sta to p re m u to . N e lla d o c u m e n ta z io n e A P I della classe
Keystroke p o te te tro v are u n e le n c o c o n i n o m i di tu tti i tasti.
C o m e sem pre, rico rd atev i di associare il ric e v ito re di eventi alla so rg e n te c o rrisp o n d e n te :

KeyListener listener = new MyKeyListener();


scene.addKeyListener(listener);

P er p o te r ric e v e re e v e n ti d ella tastiera, u n c o m p o n e n te d ev e a n c h e in v o c are:

scene.setFocusable(true);
scene.requestFocus();

Argomenti avanzati 10*6.____ ____ _________ ___ ____________ __________


Adattatori per eventi
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 in stallare u n ric e v ito re in u n a s o rg e n te di
ev e n ti d el m o u se e c o m e v e n g o n o in v o c ati i m e to d i di tale ric e v ito re q u a n d o si v erifica u n
ev en to . S o lita m e n te u n p ro g ra m m a n o n è in teressato a tu tte le seg n alazio n i p o te n z ia lm e n te
d e s tin a te a ric e v ito ri: p e r ese m p io , u n p ro g ra m m a p u ò essere in te ressa to so lta n to ai “ c lic k ”
d el m o u se , senza c h e ab b ia rile v a n z a il fa tto c h e q u e sti siano, in realtà, c o m p o s ti d a u n a
c o p p ia di e v e n ti di p re ssio n e e di rila scio di u n p u lsa n te d el m o u se . N a tu r a lm e n te il
p ro g ra m m a to re p o tre b b e p ro g e tta re u n ric e v ito re c h e d efin isca tu tti i m e to d i a cu i n o n
è in te re ssa to c o m e m e to d i “ c h e n o n fa n n o n u lla ” , c o m e in q u e s to e se m p io :

class MouseClickListener implements MouseListener


{
public void mouseClicked(MouseEvent event)
{
// reagisce al click del mouse

} * * *
I nterfacce 565

11 quattro metodi che non fanno nulla


public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
}

P e r ev itarci tu tto q u e s to lavoro, q u a lc h e a n im a b u o n a h a c re a to la classe MouseAdapter c h e


im p le m e n ta T in te rfa c d a MouseListener in m o d o c h e tu tti i m e to d i n o n fa c c ia n o nulla.
P o te te estendere tale classe, e re d ita n d o i m e to d i “ n u lla fa c e n ti” e so v ra sc riv e n d o s o lta n to
q u e i m e to d i c h e vi in te ressa n o , in q u e s to m o d o :

class MouseClickListener extends MouseAdapter


{
public void mouseClicked(MouseEvent event)
{
// reagisce al click del mouse

}
}

E siste a n c h e la classe KeyAdapter c h e im p le m e n ta l’in te rfa c c ia KeyListener c o n tre m e to d i


c h e n o n fa n n o nu lla.

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-

g e n te s e g re to p e r gli stessi m o tiv i: l ’ac ce sso a c o n t e n u ti m u ltim e d ia li project.html), c h e h a l’o b ie ttiv o di


se u n ’a z ie n d a fallisce o d e c id e di (lib ri, m u sica o v id e o ) a c q u ista ti in c r e a r e u n a v e r s io n e i n t e r a m e n t e
n o n f o rn ir e p iù s u p p o r to a u n d e ­ p re c e d e n z a . Se tale so ftw a re v ie n e a p e rta e lib era di u n sistem a o p era tiv o
te r m in a to p r o g ra m m a , i su o i u te n ti e se g u ito su u n se rv e r o su u n d isp o ­ c o m p a tib ile c o n U N I X : il sistem a
s o n o lasciati in b alìa d i se stessi e sitivo embedded (cio è “ in te g r a to ” in o p e ra tiv o G N U . T u tti i p ro g ra m m i
n o n sa ra n n o in g r a d o di c o rre g g e re u n h a rd w a re d e d ic a to ), l’u te n te n o n del p r o g e tto G N U v e n g o n o d is tri­
gli e r r o r i p re s e n ti n e l so ftw a re , n é n e p u ò m o d ific a re il c o m p o rta m e n to . b u iti so tto la licen za G N U G P L (G e ­
d i a d a tta r l o a u n n u o v o s is te m a In u n su o a rtic o lo , http://gnu.org/ n era l P u b lic L ic en se ), c h e c o n s e n te
o p e r a tiv o . F o r t u n a t a m e n t e , p e r ò , p h i l o s op hy /f re e- so ftw ar e-even-more- di c o p ia re il c o d ic e , di m o d ific a rlo e
m o lti p a c c h e tti s o ftw a re v e n g o n o important, en.html, R ic h a r d S tallm an , di d is trib u ire i p ro g ra m m i in fo rm a
566 C apitolo 10

o rig in a le o m o d ific a ta , a n c h e fa c e n ­ il Java D e v e lo p m e n t K it è d is p o ­ p r a ttu tto in q u e i se tto ri c h e s o n o di


d o li p ag are se c o n d o le ric h ie s te del n ib ile c o n G P L , m a a z ie n d e c h e in te resse p e r i p r o g ra m m a to ri, c o m e
m e rc a to , c o n l’u n ic o v in c o lo c h e tali n e c e s s ita n o d i a g g i o r n a m e n t i d i il sistem a o p e ra tiv o L in u x , i se rv e r
v ersio n i d o v ra n n o so tto stare c o m u n ­ s ic u re z z a p e r v e rs io n i m e n o r e c e n ti w e b e gli s tr u m e n ti di p ro g ra m m a ­
q u e alla lic en z a G P L . In sin tesi, b iso ­ O d i a ltre f o rm e d i s u p p o r to p a g a n o zio n e .
gna d istrib u ire, in siem e ai p ro g ra m m i O ra c le . La c o m u n ità d i sv ilu p p o di p r o ­
m o d ific a ti, a n c h e il relativ o c o d ic e A v o lte il so ftw a re o p e n so u rc e g e tti o p e n so u rc e p u ò essere m o lto
so rg e n te e c o n s e n tire a c h iu n q u e di n o n è “ p u l i t o ” c o m e il s o f tw a r e c o m p e titiv a e c re ativ a . È p iu tto s to
agire n ello stesso m o d o , m o d ific a n d o c o m m e rc ia le , p e rc h é m o lti d ei p r o ­ f re q u e n te v e d e re p ro g e tti c h e c o m ­
u lte r io r m e n te il c o d ic e . La lic en z a g ra m m a to ri co in v o lti so n o v o lo n ta ri, p e t o n o tra lo ro , a n c h e c o p ia n d o s i
G P L c o stitu isce u n c o n tra tto sociale: c h e so n o in teressati a riso lv e re p ro p ri id e e l’u n l’altro, d iv e n ta n d o così tu tti
gli u tiliz z a to ri d el so ftw are g o d o n o p ro b le m i e n o n a p ro g e tta re u n p r o ­ m ig lio r i in p o c o te m p o . Il fa tto c h e
della lib e rtà di u tilizz arlo e di m o d i­ d o tto c h e sia d i facile u tiliz z o a n c h e sia n o c o in v o lti m o lti p ro g ra m m a to ri
ficarlo, m a, in c a m b io , s o n o o b b lig a ti p e r altre p e rso n e . Il so ftw are o p e n e c h e tu tti a b b ia n o il c o d ic e s o rg e n ­
a c o n d iv id e re i m ig lio ra m e n ti c h e so u rc e h a a v u to g ra n d e successo so - te c o m p le to a d isp o siz io n e , c o n s e n te
a p p o rta n o . spesso d i in d iv id u a re e c o r r e g g e re
A lc u n e a z ie n d e c h e v e n d o n o gli e r r o r i m o lto r a p id a m e n te . E ric
so ftw a re h a n n o r ip e t u ta m e n te a t­ R a y m o n d h a d e s c r itto lo s v ilu p p o
ta c c a to la lic e n z a G P L , a c c u s a n d o la o p e n s o u rc e in u n fa m o so a rtic o lo ,
di essere “ v ir a le ” e di “ m in a r e alle “ T h e C a th e d r a l a n d th e B a z a a r ”
f o n d a m e n ta il s e tto r e d e l s o ftw a ­ (h t t p : / / c a t b . o r g / ~ e s r / w r i t i n g s / c a -
re c o m m e r c i a le ” , m a a ltre h a n n o thedral-bazaar/), d e l q u a le è c e le b re
a d o tta to u n a s tra te g ia p iù sfu m a ta , la frase “ G iv e n e n o u g h ey eb alls, all
p r o d u c e n d o so ftw a re lib e ro o p p u r e b u g s are s h a llo w ” {con un numero suf­
o p e n s o u rc e , fa c e n d o s i p a g a re p e r
Richard Stallman, un pioniere
ficiente di occhi, tutti gli errori vengono
a ttiv ità di s u p p o r to o p e r p ro g e tta re del m ovim ento per il codice aperto agalla).
e s te n s io n i p r o p rie ta rie . A d e s e m p io . e libero

Riepilogo degli obiettivi di apprendimento


Per rendere disponibile un servizio a più classi si usano le interfacce
• In Java u n tip o in terfaccia dichiara i m e to d i c h e p o s so n o essere in v o ca ti c o n un a variab ile di
q u el tipo.
• Per in d icare ch e una classe im p le m e n ta u n ’interfaccia si usa la parola riservata implements.
• I tipi in terfaccia v e n g o n o utilizzati p er ren d ere il c o d ic e m a g g io r m e n te riu tilizzab ile.

Come si effettuano conversioni tra classi e interfacce


• Si p o s s o n o effettu are c o n v e r s io n i dal tip o di u n a classe al tip o di u n ’in terfaccia c h e sia im ­
p lem en tata dalla classe.
• Le in v o c a z io n i di m e to d i m e d ia n te u n a v a ria b ile di tip o in te rfa c cia s o n o p o lim o r fic h e : il
m e to d o da in vocare v ie n e d e te r m in a to du ran te l ’e s e c u z io n e d el program m a.
• Per con v ertire un r ife r im e n to di tip o interfaccia in u n r ife r im e n to di tip o classe serve u n cast.
I nterfacce 567

Llnterfaccia Comparable della libreria standard


• Im p lem en ta te l ’interfaccia Comparable in m o d o c h e gli o g g e tti d elle vostre classi possan o essere
co n fro n ta ti tra loro, ad e se m p io in u n m e to d o di o r d in a m e n to .

Uso delle interfaeee per realizzare callback


• Il m e c c a n is m o di c a llb a c k c o n s e n te di sp ec ific a re c o d ic e c h e verrà e s e g u ito in u n s e c o n d o
m o m e n to .

Con le classi interne si limita Lambito di visibilità di classi ausiliarie


• U n a classe in tern a v ie n e dichiarata d en tro u n ’altra classe.
• Le classi in tern e so n o so lita m en te utilizzate p er classi c o n s c o p o m o lto lim itato, c h e n o n h a n n o
b iso g n o di essere v isib ili in altre z o n e del program m a.

Gli oggetti semplificati (m ock) vengono usati nel collaudo


• U n o g g e t t o s e m p lific a to (“ m o c k o b je c t”) fo rn isce gli stessi servizi di u n altro o g g e tto , m a in
m o d o sem p lifica to .
• La classe sem p lificata e la classe c o m p leta im p le m e n ta n o la m e d e sim a in terfaccia.

Eventi e loro ricevitori per la programmazione di interfacce grafiche


• G li e v e n ti d e ll’in te rfa c cia u te n te c o m p r e n d o n o p r e ssio n i di tasti, m o v im e n t i d el m o u s e e
p ression i di su o i p u lsan ti, se le z io n i di v o c i in m e n u e co sì via.
• U n r ic e v ito r e di e v e n ti è esem p la re di un a classe p rogettata dal p ro g ra m m a to re d e ll’a p p li­
c a z io n e e i su o i m e to d i d e sc r iv o n o le a z io n i da c o m p ie r e q u a n d o si v erifica u n p articolare
tip o di e v en to .
• Le so r g e n ti di e v en ti g e n e r a n o se g n a la z io n i relative agli ev en ti: q u a n d o n e a v v ie n e u n o , lo
seg n a la n o a tu tti i r ic e v ito r i interessati.
• U sa te c o m p o n e n ti di tip o DButton p er realizzare pu lsanti grafici e associate un ActionListener
a o g n i pu lsante.
• I m e to d i di una classe intern a p o sso n o acced ere alle variabili d ell’a m b ito di visibilità circostante.
• Le variabili locali a cu i si a c ce d e da u n m e to d o di una classe in tern a n o n d e v o n o essere m o ­
d ificate d o p o essere state in izializzate.

Progettazione di applicazioni grafiche con pulsanti


• U s a te u n c o n t e n it o r e d i tip o DPanel p er raggruppare in sie m e più c o m p o n e n ti d e ll’interfaccia
grafica.
• Le a zio n i c o n se g u e n ti alla p ressio n e di u n p u lsan te g ra fico v a n n o sp ecifica te m e d ia n te classi
c h e im p le m e n ta n o l ’in terfaccia ActionListener.

Temporizzatori per realizzare animazioni


• U n te m p o riz za to r e gen era ev en ti a intervalli di te m p o fissi.
• Il m e to d o repaint c h ie d e a u n c o m p o n e n te di rid isegn are se stesso: in v o c a telo o g n i volta c h e
m o d ific a te le fo r m e g ra fich e c h e v e n g o n o d iseg n a te dal m e to d o paintComponent.

Programmi che elaborano eventi del mouse


• Per catturare gli ev en ti d el m o u s e usate u n r ic ev ito r e di ev en ti d el m o u s e ( m o u s e lis te n e r ) .
568 C apitolo 10

Elementi di libreria p re s ta ti in questo capitolo ti- VAvT VÀVl/^• WG

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

Jsercizi di riepiioao e approfondimento


R l O . l . La variab ile a di tip o int c o n tie n e il valore d u e m iliardi e alla variab ile b ,a n c h ’essa di tip o
int, v ie n e assegn ato il valore d e ll’esp ression e -a. Q u a l è il risultato di a - b? E di b - a? Q u a l è il
risu ltato di Integer.compare(a, b)? E di Integer.compare(b, a)?

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)?

R I O .3 . S u p p o n e te c h e C sia un a classe c h e im p le m e n ta le in terfa cce l e ! . Q u a li d ei se g u e n ti


a ssegn am en ti r ic h ie d e u n cast?

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

* R I O .5 . S u p p o n e te c h e Sandwich sia un a classe c h e im p le m e n ta l ’in terfaccia Edible e c h e sian o date


q u este d ich ia ra zio n i di variabili:

Sandwich sub = new Sandwich();


Rectangle cerealBox = new Rectangle(s, 10, 20, 30);
Edible e = nuli;

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;

R I O .6 . Le classi Rectangle2D.Double, Ellipse2D.Double e Line2D.Double im p le m e n ta n o l ’in terfaccia


Shape. La classe Craphics2D d ip e n d e d all’in terfaccia Shape, m a n o n dalle classi c h e d e sc r iv o n o i ret­
ta n g o li, le ellissi e i se g m en ti.T r a cc ia te u n diagram m a U M L c h e illustri questa situ a zio n e.

R I O .7 . S u p p o n e te c h e r c o n te n g a u n r ife r im e n to a new Rectangle(5, 10, 20, 30). Q u a li di q u esti


a sseg n am en ti s o n o validi? C o n tro lla te la d o c u m e n ta z io n e A P I della libreria p er verificare quali
in terfa cce sian o im p le m e n ta te dalla classe Rectangle.

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;

RIO.8. Classi c o m e Rectangle2D.Double, Ellipse2D.Double e Line2D.Double im p lem e n ta n o l’interfaccia


Shape, la q u ale ha u n m e to d o

Rectangle getBounds()

c h e restitu isce il r etta n g o lo m in im o c h e ra cch iu d e la form a. E sam in ate l ’in v o c a zio n e :

Shape S = . . .;
Rectangle r = s.getBounds();

e sp ieg ate p erch é q u esto è u n e se m p io di p o lim o rfism o .

RIO.9. S u p p o n e te di d o v er elaborare u n array di d ip e n d e n ti p er trovarn e il salario m e d io . S p iegate


cosa d o v e te fare p er usare il m e to d o Data.average v isto n el Paragrafo 1 0 .1 , c h e elab ora o g g e tti di
tip o Measurable. C o sa d o v e te fare p er usarn e la se c o n d a im p le m e n ta z io n e , vista n el Paragrafo 10.4?
Q u a le s o lu z io n e è p iù sem p lice?

RIO. 10. C o sa su c c e d e se cercate di usare u n array di o g g e tti String c o n il m e to d o Data.average


v isto n el Paragrafo 10.1?

** RlO.ll. C o m e si p u ò usare il m e to d o Data.average v isto n e l Paragrafo 1 0 .4 p er calcolare la lu n ­


g h e z za m ed ia di u n in sie m e di stringhe?
570 C apitolo 10

** R I O . 1 2 . C osa su c c e d e se si fo r n isc e un array di strin g h e c o m e a r g o m e n to al m e to d o Data.average


v isto n el Paragrafo 10.4?

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;

public void m(final int x, int y)


{
int a;
final int b;

class C implements I
{
public void f()
{

} ' ' '

final int c;

R I O .1 4 . C o sa su c c e d e q u a n d o una classe in tern a cerca di a cced ere a una variab ile lo ca le c h e


assum e p iù valori diversi? P rovate e date una sp ie g a z io n e di c iò c h e osservate.

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)?

^ R l 0 .1 6 ( g r a f i c a ) . C o s ’è un o g g e tto c h e rappresenta un e v en to ? C o s ’è una so r g e n te di ev en to ?


C o s ’è u n r icev ito re di even ti?

* R I O .17 ( g r a f i c a ) . D al p u n to di vista di un p rogram m atore, qual è la differen za più im p o r ta n te


fra l ’in terfaccia u te n te di u n ’a p p lic a zio n e p er c o n s o le e qu ella di u n ’a p p lic a zio n e grafica?

* R I O . 18 ( g r a f i c a ) . S p ieg a te la d ifferen za fra un o g g e tto ActionEvent e un o g g e tto MouseEvent.

R I O .19 ( g r a f i c a ) . S p ieg a te p erch é l ’interfaccia ActionListener ha u n so lo m e to d o , m en tre Mouse-


Listener n e ha c in q u e.

** R I O .2 0 ( g r a f i c a ) . U n a classe p u ò essere u n ’o r ig in e di e v e n to per p iù tipi di ev en to ? S e sì, fo rn ite


u n e se m p io .

R I O .21 ( g r a f i c a ) . Q u a li in fo r m a z io n i c o n tie n e un o g g e tto ActionEvent? Q u a li in fo r m a z io n i


a g g iu n tiv e so n o c o n te n u te in u n o g g e tto MouseEvent?

kkk R I O .2 2 ( g r a f i c a ) . P erch é u tiliz z ia m o classi in te rn e p er i r ic e v ito r i di even ti? SeJava n o n c o n s e n ­


tisse di usare classi in te rn e, p o tr e m m o u g u a lm e n te realizzare r ic e v ito r i di even ti? In q u ale m o d o ?

R I O . 2 3 (grafica). Q u a l è la differenza fra i m e to d i paintComponent e repaint.

* R l 0 .2 4 ( g r a f i c a ) . Q u a l è la differen za fra u n fram e e un p an n ello?


I nterfacce 571

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:

public static Measurable max(Measurable[] objects)

E 1 0 . 2 . P rogettate una classe Quiz c h e im p le m e n ti l ’in terfaccia Measurable. U n q u estio n a rio ha un


p u n te g g io e u n v o to in lettere (c o m e B+). U sa te la classe Data d e ll’E sercizio E lO .l p er elaborare
u n array di q u estio n a ri, v isu a lizza n d o il p u n te g g io m e d io e il q u estio n a rio c o n il p u n te g g io più
alto, sia in lettere c h e in n u m er i.

E 1 0 . 3 . P rogettate u n a classe Person: u n a p erson a è caratterizzata da un n o m e e u n ’altezza in c e n ti-


m etri. U sa te la classe Data d e ll’E sercizio E 10.1 p er elaborare u n array di o g g e tti Person, visu alizzan d o
l ’altezza m ed ia d e lle p e r so n e e il n o m e della p erson a p iù alta.

E 1 0 . 4 (per J a v a 8). A g g iu n g e te all’in terfaccia Measurable i m e to d i statici largest e smallest, p ro­


g ettati in m o d o da restituire, risp ettiv a m en te, l ’o g g e tto c o n m isura m assim a e m in im a in u n array
di o g g e tti di tip o Measurable.

E 1 0 .5 (per J a v a 8 ) . A g g iu n g e te all’interfaccia Sequence vista nella s e z io n e E sem p i c o m p le ti 10.1


i se g u e n ti m e to d i statici:

static Sequence powersOf(int n)


static Sequence multiplesOf(int n)

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 .

E 1 0 .6 (per J a v a 8 ) . A g g iu n g e te all’in terfaccia Sequence vista nella se z io n e E sem p i c o m p le ti 10.1


u n m e to d o p r e d e fin ito c h e restituisca u n array c o n te n e n te i p rim i n va lo ri della seq u en za:

default int[] values(int n)

E 1 0 . 7 (per J a v a 8). Fate in m o d o c h e, n e ll’interfaccia Sequence vista nella s e z io n e E sem p i c o m p le ti


10.1, il m e to d o process d iv en ti un m e to d o p red efin ito.

E 1 0 . 8 . 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,


s e c o n d o q u a n to m isu rato dal m isu ratore r ic e v u to c o m e param etro:

public static Object max(0bject[] objects, Measurer m)

E 1 0 . 9 . U s a n d o u n d iverso o g g e tto Measurer, trovate in u n in sie m e di o g g e tti Rectangle q u e llo c o n


il p e r im e tro m a g g io re.

E lO .lO . M o d ific a te la classe Coin vista n el C a p ito lo 8 in m o d o c h e im p le m e n ti l ’interfaccia Com­


parable.

E l O . l l . R is o lv e te n u o v a m en te l ’E sercizio E lO .9 , fa c en d o in m o d o c h e l ’o g g e tto di tip o Measurer


sia esem p lare di un a classe in tern a d efin ita n el m e to d o main.

E l O . 1 2 . R is o lv e te n u o v a m e n te l’E sercizio E lO .9, fa c en d o in m o d o c h e l ’o g g e tto di tip o Measurer


sia esem p lare di un a classe in tern a d efin ita all’e ster n o del m e to d o main.

E lO . 13. R e a liz z a te un a classe Bag c h e rappresenti un a borsa p er la spesa e m e m o r iz z i gli a rticoli


acquistati so tto form a di strin g h e. G li articoli p o s so n o essere rip etu ti e d e v o n o essere p resen ti
572 C apitolo 10

m e to d i p er a g g iu n g er e u n a r tic o lo e p er co n tare q u an te v o lte u n d ato a r tic o lo è stato in se r ito


nella borsa:

public void add(String itemName)


public int count(String itemName)

G li articoli d e v o n o essere m e m o r iz z a ti in un a lista di tip o ArrayList<Item>, d o v e Item è una classe


in tern a c o n d u e variabili di esem p lare: il n o m e d e ll’a r tic o lo e la sua quantità.

E 1 0 .1 4 . R e a liz z a te un a classe Grid c h e m e m o r iz z i m isu re all’in te r n o di una griglia rettangolare.


La g riglia ha u n dato n u m er o di r ig h e e di c o lo n n e e si p u ò a g g iu n g er e una stringa d escrittiva a
ciascu n a sua p o s iz io n e . P rogettate i se g u e n ti m e to d i e costru ttori:

public Crid(int numRows, int numColumns)


public void add(int row, int column. String description)
public String getDescription(int row, int column)
public ArrayList<Location> getDescribedLocations()

d o v e Location è una classe in tern a c h e incapsu la il n u m er o di riga e il n u m er o di c o lo n n a di una


p o s iz io n e della griglia.

E 1 0 .1 5 . R is o lv e te di n u o v o l ’ese rc iz io p r e c e d e n te im m a g in a n d o c h e la griglia sia illim itata. Il


costru tto re n o n ha param etri e le variabili p aram etro d ei m e to d i add e getDescription, c h e so n o
n u m er i di riga e di c o lo n n a , p o s so n o essere n u m er i in teri qualsiasi.

E 1 0 .1 6 ( g r a f i c a ) . S criv ete un m e to d o randomShape c h e g e n e r i casu a lm en te o g g e tti c h e im p le m e n ­


tan o l ’interfaccia Shape della libreria standard di Java: u n m isc u g lio di retta n g o li, ellissi e se g m en ti,
c o n p o s iz io n i casuali. In v o ca telo 10 v o lte e d iseg n a te tu tte le fo rm e.

E 1 0 .1 7 ( g r a f i c a ) . M ig lio ra te il p rogram m a ButtonViewer in m o d o c h e visu alizzi il m e ssa g g io “ I


w as click ed n tim es!” o g n i volta c h e v ie n e p r e m u to il pulsante: il valore di n d eve au m en tare a
o g n i p ression e.

E 1 0 .1 8 ( g r a f i c a ) . M ig lio ra te il p rogram m a ButtonViewer in m o d o c h e abbia d u e p u lsanti, cia scu ­


n o d ei quali visu alizzi il m e ssa g g io “ I w as c lic k e d n tim es!” o g n i volta c h e v ie n e p rem u to. O g n i
p u lsan te d ev e avere il p ro p rio c o n ta to re.

E 1 0 .1 9 ( g r a f i c a ) . M ig lio ra te il program m a ButtonViewer in m o d o c h e abbia d u e pulsanti etich ettati


c o m e A e B , c ia scu n o d ei qu ali, q u a n d o p rem u to , visu alizzi il m e ssa g g io “ B u tto n x w as c lic k e d !” ,
d o v e X è A O B.

E 1 0 .2 0 ( g r a f i c a ) . R e a liz za te il p rogram m a ButtonViewer d e ll’ese rc iz io p r e ce d e n te u san d o una sola


classe c o m e r ic ev ito r e di ev en ti.

E 1 0 .2 1 ( g r a f i c a ) . M ig lio ra te il p rogram m a ButtonViewer in m o d o c h e v isu alizzi l’istante di te m p o


in cu i è avven u ta la p ression e d el pu lsante.

E 1 0 .2 2 ( g r a f i c a ) . R e a liz za te la classe AddInterestListener del p rogram m a InvestmentViewerl in


m o d o c h e sia una classe n o rm a le, n o n una classe in tern a. S u g g e r im e n to : usate un r ife r im e n to al
c o n to b an cario, a g g iu n g e n d o al r ic ev ito r e u n costru tto re c h e lo in izia lizzi.

E 1 0 .2 3 ( g r a f i c a ) . R e a liz za te la classe AddInterestListener del p rogram m a InvestmentViewer2 in


m o d o c h e sia un a classe n o r m a le, n o n una classe in tern a. S u g g e r im e n to : usate r ife rim e n ti al c o n to
b an cario e all’e tich etta , a g g iu n g e n d o al r ic ev ito r e u n c o stru tto re c h e li in izializzi.

E 1 0 .2 4 ( p e r J a v a 8 ) . R e a liz z a te di n u o v o il p rogram m a del Paragrafo 1 0 .7 .2 sp ec ific a n d o il


r ic ev ito r e m e d ia n te u n ’esp ression e lam bda.
Interfacce 573

E10.25 (per Java 8 ) . R e a liz za te di n u o v o il p rogram m a d el Paragrafo 1 0 .8 sp ec ific a n d o il r ic e ­


v ito re m e d ia n te u n ’esp ression e lam bda.

E10.26 (per Java 8 ) . R e a liz z a te di n u o v o il p rogram m a d el Paragrafo 1 0 .9 sp ec ific a n d o il r ic e ­


v ito re m e d ia n te u n ’esp ression e lam bda.

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:

Date now = new Date();


System.out.printIn(now);

★ ★ ★ EIO.28 (grafica). M o d ific a te la classe RectangleComponent d el p rogram m a di a n im a z io n e v isto nel


Paragrafo 1 0 .9 in m o d o c h e il retta n g o lo rim b alzi ai b ord i d el c o m p o n e n te , in v e ce di uscire.

* E10.29 (grafica). M o d ific a te la classe RectangleComponent2 del p rogram m a di a n im a z io n e v isto nel


Paragrafo 1 0 .1 0 in m o d o c h e o g n i “ c lic k ” del m o u s e p ro v o ch i l ’a ggiu n ta di u n n u o v o r etta n g o lo al
c o m p o n e n te visu alizzatore. S u g g e r im e n to : u s M u n ArrayList<Rectangle> e d isegn ate tutti i rettan goli
all’in te r n o d el m e to d o paintComponent.

* E10.30. P rogettate una classe Person c h e im p le m e n ti l ’in terfaccia Comparable. Le p e r so n e v a n n o


co n fro n ta te sulla base del loro n o m e . C h ie d e te all’u te n te di forn ire d ie c i n o m i e g en era te d ieci
o g g e tti Person, p o i, u san d o il m e to d o compareTo, in d iv id u a te la p rim a e l ’u ltim a person a e v isu aliz­
za ten e i n o m i.

Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
11
Ingresso/usdta
e gestione delle eccezioni

, ri

• E ssere in g ra d o di le g g e re e sc riv e re file di te sto


• E la b o ra re gli a rg o m e n ti fo rn iti sulla rig a d e i c o m a n d i
• Im p a ra re a la n cia re e a c a ttu ra re e c c e z io n i
• S ap er realizzare p ro g ra m m i c h e p ro p a g a n o e c c e z io n i a c o n tro llo o b b lig a to rio

In q u e s to c a p ito lo im p a re re te a le g g ere e sc riv e re file: u n ’ab ilità v e ra m e n te im p o r ta n te


p e r l’e la b o ra z io n e di d ati reali. C o m e a p p lic a z io n e di q u e s to , v e d re te c o m e cifrare i dati.
L’u ltim a p a rte d el c a p ito lo sp ieg a c o m e i p ro g ra m m i p o ssa n o seg n alare p ro b le m i, c o m e la
m a n c a n z a di file o l’a c q u isiz io n e di d ati n o n c o r r e tti, e rip re n d e r e l’e la b o ra z io n e , u sa n d o
il m e c c a n ism o di g e s tio n e d elle e c c e z io n i del lin g u a g g io Java.
576 C apitolo 11

11.1 Leggere e scrivere file di testo


In iz ia m o q u e s to c a p ito lo p a rla n d o della fre q u e n te n ec essità di le g g e re e sc riv e re file
c o n te n e n ti in fo rm a z io n i d i tip o te stu a le (“ file d i te s to ”), c o m e i file di c o d ic e so rg e n te
Java O i file H T M L o p p u re , a n c o ra , i file c h e v e n g o n o cre a ti c o n u n se m p lic e e d ito r di
testi c o m e W in d o w s N o te p a d .
Per leggere file di testo usate Il m o d o p iù se m p lic e p e r le g g e re u n file d i te sto p re v e d e !’u tiliz z o della classe Scanner,
la classe Scanner. c h e av ete già v isto p e r la le ttu ra di d ati in in g resso p ro v e n ie n ti dalla tastiera. P e r le g g e re
d ati da u n file p re se n te sul d isco , la classe Scanner si affida a u n ’altra classe, File, c h e d escriv e
file e c a rte lle p re se n ti in u n d isc o (la classe File h a m o lti m e to d i di c u i n o n p a rlia m o in
q u e s to lib ro , ad e s e m p io p e r c a n c e lla re u n file o p e r m o d ific a rn e il n o m e ).
P er p rim a cosa si c o s tru isc e u n o g g e tto di tip o File, f o rn e n d o il n o m e d el file da
leg g ere;

File inputFile = new File("input.txt");

S u c c e ssiv a m e n te si u tilizz a tale o g g e tto p e r c o s tru ire u n o g g e tto Scanner:

Scanner in = new Scanner(inputFile);

Q u e s to o g g e tto Scanner le g g e il te sto c o n te n u to n el file input.txt: p e r a c q u isire dati p o te te


usare i c o n s u e ti m e to d i della classe, c o m e next, nextLine, nextint e nextDouble.
A d ese m p io , il c ic lo s e g u e n te p u ò essere u tiliz z a to p e r e la b o ra re n u m e ri p re se n ti in
u n file d i testo :

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 .

Se il file in c u i sc riv e re esiste già, v ie n e sv u o ta to p rim a di sc riv e rv i n u o v i d ati. Se il file


n o n esiste, v ie n e c re a to u n file v u o to .
La classe PrintWriter c o stitu isc e u n m ig lio ra m e n to d ella classe PrintStream, c h e g ià
c o n o s c e te p e rc h é System.out n e è u n e sem p lare. C o n u n o g g e tto di tip o PrintWriter p o te te
usare i c o n s u e ti m e to d i print, println e printf:

out.printIn("Hello, World!");
out.printf("Total: %8.2f\n", total);

Quandoavetefinitodielaborare Q u a n d o avete te rm in a to di e la b o ra re u n file, a c c e rta te v i di chiudere l’o g g e tto S c a n n e r o


un file, chiudetelo. P r in t W r it e r :

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 :

public static void main(String[] args) throws FileNotFoundException

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;

Questo programma legge un file contenente numeri e li scrive in


un altro file, incolonnati e seguiti dal loro totale.
*/
public class Total
578 C apitolo 11

public static void main(String[] args) throws FileNotFoundException


{
// chiede i nomi dei file di input e di output

Scanner console = new Scanner(System.in);


System, out. print ("Input file: ");
String inputFileName = console.next();
Sy stem. out. print ("Output file: ");
String OUtputFileName = console.next();

// costruisce gli oggetti Scanner e PrintWriter per leggere e scrivere

File inputFile = new File(inputFileName);


Scanner in = new Scanner(inputFile);
PrintWriter out = new PrintWriter(outputFileName);

// Acquisisce i dati in ingresso e li scrive in uscita

double total = 0;

while (in.hasNextDoubleO)
{
double value = in.nextDouble();
out.printf("%15.2f\n", value);
total = total + value;
}

out.printf("Total: %8.2f\n", total);

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

Per far pratica


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 1 . 1 ,R 1 1 .2 e E l 1 .1 ,al te r m in e del
ca p ito lo .

Errori comuni 11.1


Barre rovesciate (backslash) nei nomi di file
Q u a n d o sp e cific ate so tto fo rm a di strin g a le tte ra le il n o m e di u n file c h e c o n tie n e ca ra tte ri
“ b a rra ro v esc iata” (c o m e n e i sistem i o p e ra tiv i W in d o w s ), d o v e te in se rire c iasc u n a b a rra
rovesciata due volte:

File inputFile = new File("c:W h o m e w o r kWinput.dat'');

R ic o r d a te c h e u n a sola b a rra rovesciata all’in te r n o di s trin g h e ra c c h iu se da v irg o le tte è


u n carattere di escape, c h e v ie n e c o m b in a to c o n il c a ra tte re se g u e n te p e r assu m ere u n
sig n ific ato sp eciale, c o m e p e r e s e m p io \n, c h e ra p p re se n ta l’o p e ra z io n e “ a n d a re a c a p o ” .
La c o m b in a z io n e W d e fin isc e u n a sin g o la b a rra rovesciata.
Q u a n d o , p e rò , il n o m e di u n file è fo rn ito al p ro g ra m m a d all’u te n te , la b a rra rovesciata
n o n va d ig ita ta d u e vo lte.

-EttoriCQDiunl11.2 . ... ........ ,


Costruire uno S c a n n e r per leggere una strìnga
Q u a n d o c o s tr u i te u n o g g e tto Pr intWriter u s a n d o u n a s tr in g a c o m e p a r a m e tr o di
c o s tru z io n e , p o i scriv erà in u n file:

PrintWriter out = new PrintWriterCoutput.txt");

Q u e s to , p e rò , n o n fu n z io n a c o n u n o g g e tto Scanner. L’e n u n c ia to

Scanner in = new Scanner("input.txt"); // ERRORE ?

non ap re u n file, m a , s e m p lic e m e n te , analizza il c o n te n u to d ella strin g a : l ’in v o c a z io n e


i n . n e x t O restitu isc e la strin g a "input.txt". In a lc u n i casi (c o m e n e l P arag rafo 11.2.5) si
tra tta di u n a c a ra tte ristic a u tile.
D o v e te so lta n to ric o rd a rv i di fo rn ire al c o s tru tto r e di Scanner u n o g g e tto File:

Scanner in = new Scanner(new File("input.txt")); I l così va bene

r Argomenti avanzati 1 L l _________ ______________


Leggere pagine Web
C o n q u e sta se q u e n z a di e n u n c ia ti p o te te le g g e re il c o n te n u to di u n a p a g in a w e b :

String address = "http://horstmann.com/index.html";


URL pageLocation = new URL(address);
Scanner in = new Scanner(pageLocation.openStream());
580 C apitolo 11

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.

Aroomenti avanzati 11.2 „ ,. _____ ____ _____ __________


Finestre per la selezione di file
In u n p ro g ra m m a d o ta to d i in te rfa c c ia g rafica, spesso si v u o le c o n s e n tire all’u te n te di
se le z io n a re il n o m e di u n file m e d ia n te u n a fin e stra di d ia lo g o (dialog box), c o m e q u ella
realizzata dalla classe DFileChooser n el p a c c h e tto S w in g , d e d ic a to p ro p rio alle in te rfa c c e
g ra fic h e p e r l’in te ra z io n e c o n l’u te n te .

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

in d e x -file s Q a llc la s s e s -n o fra m e .h tm l Q o v e rv ie w -s u m m i


n iava Q c o n s ia n i-v a lu e s .h tm l Q o v e rv ie w -tre e .h ti
r H iavax Q d e p re c a te d -lis L h tm I Q p a c k a g e -lis t
Q o rg Q h e lp -d o c h tm i D s e ria liz e d -fo rm .li
resources Q in d e x h tm i Q stylesheeLcss
Q a llc la s s e s -fra m e .h tm l D o v e rv ie w -fra m e .h tm l

U _______________________________ III _____________ ^______ IL

File Name:

Files o f Iv p e :

La classe DFileChooser h a m o lte o p z io n i p e r m e tte re a p u n to la v isu a liz z a z io n e della


fin estra di d ia lo g o , m a nella sua fo rm a di base !’u tiliz z o è p iu tto s to se m p lic e: si c o s tru isc e
u n o g g e tto p e r la scelta d el file (u n ese m p la re di DFileChooser), q u in d i si in v o c a il su o
m e to d o showOpenDialog o showSaveDialog. E n tra m b i i m e to d i v isu a liz za n o la stessa fin estra di
d ia lo g o , m a il p u lsa n te p e r se le z io n a re u n file si c h ia m a , r is p e ttiv a m e n te ,“ O p e n ” (“ A p r i” )
O “ S ave” (“ S alva” ).
P e r u n p o s iz io n a m e n to m ig lio re d ella fin e stra di d ia lo g o su llo s c h e r m o p o te te
specificare il c o m p o n e n te d e ll’in terfaccia u te n te al di so p ra del q u ale v o le te a p rire la finestra
di d ia lo g o ; se n o n vi im p o rta d o v e si ap re la fin estra di d ia lo g o p o te te usare se m p lic e m e n te
nuli. 1 m e to d i showOpenDialog e showSaveDialog re s titu is c o n o DFileChooser.APPROVE_OPTION se
l’u te n te ha sc e lto u n file, DFileChooser.CANCEL_0PTI0N se l’u te n te ha a n n u lla to la se lez io n e.
Se è sta to s e le z io n a to u n file, p e r o tte n e r e u n o g g e tto File c h e lo d escriv e si in v o c a il
m e to d o getSelectedFile. E c c o u n e s e m p io c o m p le to :
Ingresso/ uscita E gestione delle eccezioni 581

DFileChooser chooser = new DFileChooser();


Scanner in = nuli;
if (chooser.showOpenDialog(null) == DFileChooser.APPR0VE_0PT10N)
{
File selectedFile = chooser.getSelectedFile();
in = new Scanner(selectedFile);

} * * *

A rg o m e n tijiM iz a liiy ...................... ............................... ........... . .


Codifiche dei caratteri
U n carattere (character, c o m e la le tte ra A , la cifra 0 , la le tte ra a c c e n ta ta é, la le tte ra g re c a
1
Tl, il s im b o lo O u n o d eg li id e o g ra m m i cinesi) v ie n e c o d ific a to m e d ia n te u n a se q u e n z a
di b y te , c ia sc u n o d ei q u ali, ra p p re s e n ta to c o m e n u m e ro in te ro n o n n e g a tiv o in base d e ­
c im ale, è u n v alo re c o m p re s o tra 0 e 2 5 5 .
S fo rtu n a ta m e n te n o n esiste u n ’u n ic a c o d ifica. N e l 1 9 6 3 , A S C II (A m e ric a n S ta n d a rd
C o d e fo r I n fo r m a tio n In te rc h a n g e ) d e fin ì u n a c o d ific a p e r 128 c a ra tte ri, c h e c o m p re n d e
tu tte le le tte re m a iu sc o le e m in u s c o le d e ll’alfa b eto la tin o , le cifre d e c im a li e a lc u n i sim b o li,
c o m e + "^ %, e li ra p p re se n ta c o m e v a lo ri c o m p re si tra 0 e 127. A d e se m p io , il c o d ic e p e r
la le tte ra A è 65.
U n n u m e ro d i n a z io n i se m p re m a g g io re se n tì il b iso g n o di c o d ific a re il p r o p rio alfa­
b e to e p ro g e ttò il p r o p rio c o d ic e , spesso b asan d o si sulla c o d ific a A S C II e u sa n d o i v a lo ri
c o m p re si n e ll’in te rv a llo c h e va da 128 a 2 5 5 p e r i sim b o li c a ra tte ristic i della p ro p ria lin g u a .
A d ese m p io , in S p ag n a la le tte ra é è stata c o d ific a ta c o n il n u m e ro 2 3 3 , m a in G re c ia lo
stesso c o d ic e d e n o ta la le tte ra l (io ta m in u sc o la ). C o m e p o te te b e n im m a g in a re , se u n
tu rista sp a g n o lo d i n o m e Jo sé inviasse u n m e ssag g io di p o sta e le ttro n ic a a u n a lb e rg o
g re c o , q u e sta d iv ersa c o d ific a sa re b b e u n p ro b le m a .
P e r riso lv e re q u e sti p ro b le m i, n e l 1 987 in iz iò il p r o g e tto della c o d ific a Unicode.
C o m e d e s c ritto n ella s e z io n e C o m p u te r e so c ietà 4 .2 , q u e sta c o d ific a assegna u n n u m e ro
in te ro (n o n n e g a tiv o ) u n iv o c o a c iasc u n c a ra tte re u tiliz z a to n el m o n d o , a n c h e se e sisto n o
c o m u n q u e p iù v a ria n ti della c o d ific a b in a ria di q u e s ti n u m e ri in te r i, la p iù diffusa d elle
q u ali è d e n o m in a ta U T F - 8 e c o d ific a ciasc u n c a ra tte re c o m e u n a se q u e n z a di b y te , da
u n o a q u a ttro . A d ese m p io , la le tte ra A è a n c o ra 6 5 , c o m e in A S C II, m e n tr e la le tte ra é
v ie n e co d ific a ta c o m e 195 169. I d e tta g li d ello sc h e m a di c o d ific a n o n s o n o im p o rta n ti,
c iò c h e im p o r ta è sp e cific are, q u a n d o si le g g e e si scriv e u n file, c h e la c o d ific a a d o tta ta
è U T F -8 .
F in o ad o g g i i sistem i o p e ra tiv i Windovv^s e M a c in to s h n o n h a n n o a n c o ra a d o tta to la
c o d ific a U T F - 8 e Java u tilizz a lo stesso sc h e m a di c o d ific a d el sistem a o p e ra tiv o : se n o n si
sp ecifica q u alco sa di d iv erso , le classi Scanner e PrintWriter le g g o n o e s c riv o n o file u sa n d o
tale c o d ific a , u n a scelta effica ce se i file c o n te n g o n o so lta n to c a ra tte ri A S C II o p p u r e se
le ttu ra e s c rittu ra d el file a v v e n g o n o c o n la stessa co d ific a . Se, p e rò , d o v e te e la b o ra re
file c h e c o n te n g o n o c a ra tte ri a c c e n ta ti, id e o g ra m m i cin esi o sim b o li speciali, d o v re ste
r ic h ie d e re e s p lic ita m e n te la c o d ific a U T F - 8 , c o s tru e n d o così u n o Scanner:

Scanner in = new Scanner (file, "UTF-8");

e u n PrintWriter:
582 C apitolo 11

PrintWriter out = new PrintWriter(file, "UTF-8");

P ro b a b ilm e n te vi sta re te c h ie d e n d o c o m e m a i Java n o n sia in g ra d o d i in d iv id u a re a u to ­


n o m a m e n te la c o d ific a c o rre tta . P e r ca p irlo , p r e n d ia m o in esam e la strin g a Jo sé , c h e , in
U T F - 8 , v ie n e c o d ific a ta c o m e 7 4 111 115 195 1 6 9 . 1 p rim i tre b y te , c h e c o d ific a n o Jo s,
a p p a r te n g o n o all’in te rv a llo della c o d ific a A S C II e n o n p o n g o n o p ro b le m i, m a i d u e b y te
successivi, 195 169, p o tr e b b e ro ra p p re se n ta re la le tte ra é in U T F - 8 o p p u re la c o p p ia di
c a ra tte ri Aj n ella co d ific a tra d iz io n a le sp ag n o la. L’o g g e tto Scanner n o n c o n o s c e lo sp a g n o lo
e n o n è in g ra d o di d e c id e re q u a le sia la c o d ific a d a sceg liere.
Q u in d i, q u a n d o sc a m b ia te file c o n u te n ti di altre z o n e d el m o n d o , d o v re ste se m p re
sp e cific are e s p lic ita m e n te la c o d ifica.

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

q u e s to ciclo v isu a liz z e re b b e c iasc u n a p aro la su u n a rig a a sé sta n te :

Mary
had
a
little
lamb

U n testo , p e rò , p u ò c o n te n e re a n c h e seg n i di p u n te g g ia tu ra e a ltri sim b o li. Il m e to d o next


restitu isc e u n a se q u e n z a di c a ra tte ri qualsiasi c h e n o n sian o caratteri di spaziatura {white
space), c h e s o n o gli spazi v eri e p ro p ri {space), i c a ra tte ri di ta b u la z io n e {tab) e i c a ra tte ri di
“ n u o v a rig a ” c h e se p a ra n o le rig h e {newline). Ad ese m p io , q u e s te so n o c o n s id e ra te p a ro le
dal m e to d o next:

snow.
1729
C-H-
Ingresso/ uscita E gestione delle eccezioni 583

(o sserv ate c h e il p u n to d o p o snow è c o n s id e ra to p a rte d ella p aro la , p e rc h é n o n è u n


c a ra tte re di sp aziatu ra).
V e d ia m o in d e tta g lio co sa a c c a d e q u a n d o v ie n e in v o c a to il m e to d o next. I c a ra tte ri
in in g re ss o c h e s o n o c a r a tte r i d i sp a z ia tu ra v e n g o n o consumati, c io è v e n g o n o rim o ssi
dal flusso di in g re sso , se n z a e n tra re a fa r p a r te d ella p a ro la c h e si sta c o s tru e n d o . 11
p r im o c a ra tte re c h e risu lta essere d iv e rso da u n c a ra tte re d i sp a z ia tu ra d iv e n ta il p r im o
c a ra tte re della p a ro la . Si a g g iu n g o n o u lte r io r i c a r a tte r i fin c h é n o n si tro v a u n c a ra tte re
di sp a z ia tu ra o p p u r e si r a g g iu n g e la fin e d el file. Se, p e r ò , la fin e d el file v ie n e r a g g iu n ta
p r im a c h e sia s ta to a g g iu n to a lm e n o u n c a ra tte re alla p a ro la in c o s tru z io n e , si v e rific a
NoSuchElementException.
A v o lte si v o g lio n o le g g ere p ro p rio le p aro le n el senso c o m u n e del te rm in e , ig n o ra n d o
qualsiasi c a ra tte re c h e n o n sia u n a le tte ra . P e r farlo b iso g n a in v o c a re il m e to d o useDelimiter
d e ll’o g g e tto Scanner c h e si sta u sa n d o :

Scanner in = new Scanner(. . .);


in.useDelimiter (" [ Za -z ]+" ) ;

In q u e s to m o d o a b b ia m o sp e c ific a to c h e lo sc h e m a di c a ra tte ri (d e tto pattern) c h e separa


le p a ro le è “ q u a lu n q u e se q u e n z a di c a ra tte ri d iv ersi dalle le tte r e ” (si v ed a la se z io n e
A rg o m e n ti avanzati 11.4). C o n q u e s te im p o s ta z io n i, i seg n i d i p u n te g g ia tu ra e le cifre
n u m e ric h e v e n g o n o e lim in a te dalle p a ro le re stitu ite dal m e to d o next.

11.2.2 Acquisire caratteri


A v o lte si v u o le le g g ere u n c a ra tte re p e r vo lta, c o m e v e d re te n e ll’e s e m p io r ip o rta to n el
P arag rafo 1 1.3, d o v e e s e g u ire m o la c ifra tu ra d ei c a ra tte ri di u n file. P e r o tte n e r e q u e s to
risu lta to si in v o c a il m e to d o useDelimiter d e ll’o g g e tto Scanner u sa n d o c o m e p a ra m e tro
u n a s trin g a v u o ta :

Scanner in = new Scanner(. . , ) ;


in.useDelimiter("");

O g n i successiva in v o c a z io n e di next re stitu isc e u n a strin g a c o n te n e n te u n so lo c a ra tte re ,


p e r cu i p o ssia m o e la b o ra re i sin g o li c a ra tte ri di u n file in q u e s to m o d o :

while (in.hasNextO)
{
char eh = in.next().charAt(o);
. . . // elabora eh
}

11.2.3 Classificare caratteri


iciasse C h a ra c te r contiene Q u a n d o si ac q u isisce u n c a ra tte re in in g resso o q u a n d o si a n a liz z a n o i c a ra tte ri di u n a
metodi utili per classificare p aro la o di u n a rig a, spesso si v u o le sap ere di c h e tip o di c a ra tte re si tratta: la classe
i caratteri. Character c o n tie n e p a re c c h i m e to d i u tili a riso lv e re q u e s to p ro b le m a , c ia sc u n o d e i q u ali
ric h ie d e u n p a ra m e tro di tip o char e restitu isc e u n v alo re di tip o boolean (c o m e si p u ò
v e d e re n ella T ab ella 1).
A d ese m p io , l’in v o c a z io n e
584 C apitolo 11

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:

String line = in.nextLine();

La successiva rig a di te sto (p riv a ta d el c a ra tte re fin ale di “ n u o v a r ig a ” ) v ie n e assegnata alla


strin g a lin e : si p u ò , così, d is p o rre della rig a p e r le e la b o ra z io n i ric h ie ste .
Il m e to d o hasNextLine restitu isc e true se c ’è a lm e n o u n ’u lte r io re rig a di te sto da
a c q u isire e false q u a n d o tu tte le rig h e so n o state le tte : p e r essere sic u ri c h e ci sia u n ’altra
rig a da e la b o ra re , in v o c a te il m e to d o hasNextLine p rim a d el m e to d o nextLine.
E c c o u n e s e m p io tip ic o di e la b o ra z io n e di rig h e di u n file. U n file c o n d ati di
p o p o la z io n e , e s tra tto dal C IA F act B o o k (https://www.cia.gov/library/publications/the-
world-factbook/) c o n tie n e rig h e c o n q u e s to fo rm a to :

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
}

p o i u sia m o i m e to d i isDigit e isWhitespace della T ab ella 1 p e r s c o p rire d o v e fin isc e il n o m e


della n a z io n e e d o v e in iz ia il n u m e ro .
I ngresso/ uscita E gestione delle eccezioni 585

T ro v ia m o la p rim a cifra:

int i = 0;
while ( !Character.isDigit(line.charAt(i))) { i++; }

Q u in d i, estra ia m o il n o m e della n a z io n e e la relativa p o p o la z io n e :

String countryName = line.substring(o, i);


String population = line.substring(i);

In q u e s to m o d o , p e rò , il n o m e della n a z io n e te rm in a c o n u n o o p iù spazi, c h e e lim in ia m o


u sa n d o il m e to d o trim:

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 .

11 .2.5 Analizzare una stringa


N e l p a ra g ra fo p r e c e d e n te av e te v isto c o m e s c o m p o rr e u n a s trin g a in s o tto s tr in g h e
c e rc a n d o sin g o li c a ra tte ri sp ecifici, m a a v o lte è p iù se m p lic e se g u ire u n a p p ro c c io diverso.
P e r le g g e re i c a ra tte ri p re s e n ti in u n a strin g a si p u ò u sare u n o g g e tto Scanner:

Scanner lineScanner = new Scanner(Iine);

Q u e s to lineScanner p u ò essere u tiliz z a to c o m e qualsiasi altro Scanner, le g g e n d o p a ro le e


n u m e ri:

String countryName = lineScanner.next(); // leggiamo la prima parola


// aggiungiamo parole a contryName finché non troviamo un numero
while ( IlineScanner.hasNextInt())
{
countryName = countryName + + lineScanner.next();
}
int populationValue = lineScanner.nextlnt();
586 C a p it o l o 11

11.2.6 Convertire stringhe in numeri


A v o lte c a p ita di avere u n a strin g a c h e c o n tie n e u n n u m e ro , c o m e la strin g a population
n e l P arag rafo 1 1 .2 .4 . S u p p o n ia m o , ad e se m p io , c h e la strin g a sia c o stitu ita dalla se q u e n z a
di c a ra tte ri " 3 0 3 8 2 4 6 4 6 ". P e r o tte n e r e il n u m e ro in te ro 3 0 3 8 2 4 6 4 6 u sia m o il m e to d o
Integer.parseint:

int populationValue = Integer.parseint(population);


// ora il valore di populationValue è il numero intero 3 0 3 8 2 4 6 4 6

Se una stringa contiene le cifre P er c o n v e rtire u n a strin g a c o n te n e n te le cifre di u n n u m e ro in v irg o la m o b ile n el su o


di un numero, se ne può ottenere v alo re si usa il m e to d o Double.parseDouble. S u p p o n ia m o , ad e se m p io , c h e la strin g a input
ilvalore invocando sia u g u a le a "3.95".
Integer.parseint
oppure Double.parseDouble. double price = Double.parseDouble(input);
// ora il valore di price è il numero 3.95

Q u a n d o si in v o c a n o i m e to d i Integer.parseint o p p u re Double.parseDouble o c c o r re fare


a tte n z io n e : il lo ro p a ra m e tro d ev e essere u n a strin g a c h e c o n tie n e le cifre di u n n u m e ro ,
sen za a ltri c a ra tte ri, n e m m e n o di spaziatura! N e l n o stro e s e m p io sa p p ia m o c h e n o n ci
sa ra n n o spazi all’in iz io della strin g a , m a ce n e p o tr e b b e ro essere alla fin e, p e r c u i è m e g lio
in v o c a re p r im a il m e to d o trim:

int populationValue = Integer.parseInt(population.trim());

Q u e s to e s e m p io p ro se g u e nella s e z io n e C o n s ig li p ra tic i 11.1.

11 .2.7 Evitare errori nell'acquisizione di numeri

A v ete g ià u sa to m o lte v o lte i m e to d i nextint e nextDouble della classe Scanner, m a q u i ci


o c c u p e r e m o di q u e llo c h e su c c e d e in situ a z io n i “ a n o m a le ” . A n a liz z ia m o l’in v o c a z io n e :

int value = in.nextInt();

Il m e to d o nextint r ic o n o s c e n u m e ri c o m e 3 o - 2 1 .Se, p e rò , n e i d ati le tti dallo Scanner non


c'è un numero, si v erific a l’e c c e z io n e InputMismatchException. C o n s id e ria m o , ad e se m p io , la
situ a z io n e in c u i i d ati in in g resso s o n o q u esti:
'il.'.IL
T m m C

In v o c a n d o nextint v ie n e “ c o n s u m a to ” lo sp azio in iz ia le e v ie n e le tta la p aro la 2ist, c h e


n o n risp e tta il f o rm a to p re v isto p e r i n u m e ri: in q u e sta s itu a z io n e n el m e to d o nextint si
v e rific a l’e c c e z io n e InputMismatchException, p e r “ m a n c a ta c o r r is p o n d e n z a ” (mismatch) tra
il f o rm a to p re v isto e il f o rm a to e ffe ttiv a m e n te le tto .
Se, in v ece, q u a n d o si in v o c a n o i m e to d i nextint o nextDouble n o n c ’è a lc u n d a to
p re se n te , si v e rific a l’e c c e z io n e NoSuchElementException. P e r e v ita re e c c e z io n i, p e r ac q u isire
u n n u m e ro in te ro si an alizza il flusso di d ati in in g resso c o n il m e to d o hasNextInt, ad
e s e m p io in q u e s to m o d o :
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 587

if (in.hasNextIntO)
{
int value = in.nextlnt();

A n a lo g a m e n te , p r im a di in v o c a re nextDouble b iso g n e re b b e in v o c a re hasNextDouble.

11.2.8 Acquisire numeri, parole e righe


I m e to d i nextint, nextDouble e next non c o n s u m a n o i c a ra tte ri di sp a zia tu ra c h e s e g u o n o il
d a to a c q u isito : c iò p u ò ra p p re se n ta re u n p ro b le m a se si a lte rn a n o in v o c a z io n i di nextint/
nextDouble/next e di nextLine. Im m a g in a te , in fa tti, di avere u n file c o n te n e n te n o m i di
n a z io n i e le lo ro p o p o la z io n i, in q u e s to fo rm a to :

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

D o p o la p rim a in v o c a z io n e di nextLine, r im a n e in in g resso :

I l I 3ì 4T oT^4R ”llT5T5T^nTn'in \n

D o p o l’in v o c a z io n e di n e x tin t, rim a n e in in g resso :

MI] \n

Si o sserv i c h e l’in v o c a z io n e di nextint non c o n s u m a il c a ra tte re newline, q u in d i la s e c o n d a


in v o c a z io n e di nextLine restitu isc e u n a strin g a v u o ta !
La s o lu z io n e c o n siste n e ll’in v o c are nextLine d o p o aver le tto il v alo re della p o p o la z io n e :

String countryName = in.nextLine();


int population = in.nextlnt();
in.nextLineO; // consuma il carattere newline
588 C a p it o l o 11

L’in v o c a z io n e d i nextLine c o n s u m a tu tt i i c a ra tte ri di sp a z ia tu ra re sid u i e il c a ra tte re


newline.

11.2.9 Impaginare i dati in uscita


S pesso, q u a n d o si v isu a liz z a n o n u m e ri o s trin g h e , si v u o le fare in m o d o c h e la lo ro
im p a g in a z io n e sia s o tto il c o n tro llo d e l p ro g ra m m a to re . A d e se m p io , le q u a n tità espresse
in d o lla ri v a n n o so lita m e n te v isu a liz za te c o n d u e cifre d o p o il se p a ra to re d e c im a le , in
q u e s to m o d o :

Cookies: 3.20

F in dal P arag rafo 4 .3 .2 sa p e te c o m e o tte n e r e u n ’im p a g in a z io n e di q u e s to tip o , u sa n d o


il m e to d o printf: in q u e s to p a ra g ra fo p re s e n te re m o a lc u n e u lte r io ri o p z io n i relativ e a
tale m e to d o .
S u p p o n ia m o di v o le r v isu alizzare u n a tab ella di a rtic o li e p rez zi, m e m o riz z a ti in d u e
array, in q u e s to m o d o :

Cookies: 3.20
Linguine: 2.95
Clams: 17.29

O sse rv a te c h e le strin g h e c h e ra p p re se n ta n o gli artico li d e v o n o essere in c o lo n n a te a sinistra,


m e n tr e i n u m e r i c h e ra p p re s e n ta n o i p rez zi v a n n o in c o lo n n a ti a d estra. In m a n c a n z a di
in d ic a z io n i, il m e to d o p r in tf in c o lo n n a i v a lo ri a destra.
P er sp e cific are c h e il c o n te n u to di u n c a m p o d ev e essere in c o lo n n a to a sin istra, si
a g g iu n g e u n tr a ttin o {hyphen, -) p rim a d e ll’a m p ie z z a del c a m p o stesso:

System.out.printf(''%-I0s%l0.2f", items[i] + prices[i]);

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 :

• La sp e cific a %-lOs im p a g in a u n a s trin g a c o n in c o lo n n a m e n to a sin istra. A lla s trin g a


p r ic e s [ i] + v e n g o n o a g g iu n ti spazi in m o d o c h e d iv e n ti lu n g a d ie c i c a ra tte ri
(u n a p r o c e d u r a c h e v ie n e c h ia m a ta padding). Il s im b o lo - sp e c ific a c h e la s trin g a
d ev e essere p o sta a sin istra, se g u ita da u n n u m e ro di spazi su ffic ie n te a ra g g iu n g e re
la lu n g h e z z a di 10 c a ra tte ri.
• La specifica %lo.2f im p a g in a u n n u m e ro in virg o la m o b ile, a n c h e q u esto in u n c a m p o
largo dieci caratteri. In q u esto caso, p e rò , gli spazi v e n g o n o ag g iu n ti alla sinistra del valore.

p tr in g a allineata) lunghezza 10 lunghezza 10


a sinistra
C
n T 'a m S : 1 7 . 2 I ]

^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

U n e le m e n to sin ta ttic o c o m e % -iO s o %io.2f è d e tto specifica o sp e c ific a to re di formato e


d escriv e c o m e d e b b a essere v isu a liz za to u n valore.
U n a sp ecifica di f o rm a to h a la se g u e n te stru ttu ra :

• 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:

6 . S e il flu sso d ’in g r e s so c o n t i e n e i ca ra tteri H e llo , W o r ld !,c h e v a lo r e a s s u m o n o word e input


d o p o l ’e s e c u z io n e d i q u e s ti e n u n c ia ti?
String word = in.next();
String input = in.nextLine();
7. S e il flu sso d ’in g r e s so c o n t i e n e i ca ra tteri 995.0 Fred, c h e v a lo r e a s s u m o n o number e input
d o p o l ’e s e c u z io n e d i q u e s ti e n u n c ia ti?
int number = 0;
if (in.hasNextIntO) { number = in.nextlnt(); }
String input = in.next();
8 . S e il flu sso d ’in g r e s so c o n t i e n e i caratteri 6E6 $ 6 ,9 9 5 .0 0 , c h e v a lo re a s s u m o n o x l e x2 d o p o
l ’e s e c u z i o n e di q u e s ti e n u n c ia ti?
590 C a p it o l o 11

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 avanzati 11.4 ________________ _____ ______


r Espressioni canoniche (regular expressions)
U n a espressione canonica (regular expression) d e sc riv e u n o sc h e m a (pattern) d i c a ra tte ri.
P e r e s e m p io , i n u m e r i h a n n o u n f o r m a to s e m p lic e , c o n t e n g o n o u n a o p iù cifre:
l’esp re ssio n e c a n o n ic a c h e d e sc riv e n u m e ri è [0-9]+. La n o ta z io n e [0-9] in d ic a “ qualsiasi
cifra c o m p re sa fra 0 e 9 ” , m e n tr e il se g n o + sig n ifica “ u n o o p iù ” di tali e le m e n ti.
Le fu n z io n i di ric e rc a degli a m b ie n ti di p ro g ra m m a z io n e professionali so n o in g ra d o di
in te rp re ta re le esp re ssio n i c a n o n ic h e e n u m e ro s i p ro g ra m m i di se rv iz io u sa n o esp ressio n i
c a n o n ic h e p e r in d iv id u a re c o r r is p o n d e n z e te stu a li. U n p ro g ra m m a m o lto d iffu so c h e
usa esp re ssio n i c a n o n ic h e è grep (il c u i n o m e è !’a c ro n im o di “ g lo b a l re g u la r e x p re ssio n
p r i n t ” , c io è v isu a liz z a z io n e di esp re ssio n i c a n o n ic h e g e n e ra liz z a te ). P o te te e se g u ire grep
da u n a fin estra di c o m a n d i o all’in te r n o di a lc u n i a m b ie n ti d i c o m p ila z io n e : fa p a rte del
sistem a o p e ra tiv o U N I X , m a n e e sisto n o v e rsio n i a n c h e p e r W in d o w s . Il p ro g ra m m a
r ic h ie d e u n ’esp re ssio n e c a n o n ic a e il n o m e di u n o o p iù file in c u i cercare: d u ra n te la
p ro p ria e s e c u z io n e visu alizza u n e le n c o d elle rig h e c h e c o r r is p o n d o n o all’e sp re ssio n e
c a n o n ic a .
S u p p o n ia m o d i v o le r c e rc a re tu tt i i “ n u m e r i m a g ic i” ( in tr o d o tti n e lla s e z io n e
S u g g e rim e n ti p e r la p ro g ra m m a z io n e 4 .1) p re se n ti in u n file. Il c o m a n d o s e g u e n te e le n c a
tu tte le rig h e d el file Homework.Java c h e c o n te n g o n o se q u e n z e di cifre:

grep [0-9]+ Homework.java

Il risu lta to n o n è p a r tic o la rm e n te u tile, p e rc h é e le n c a a n c h e le rig h e c h e c o n te n g o n o


n o m i di v aria b ili, c o m e xi. E c h ia ro ch e , in v ece, c e rc h ia m o le se q u e n z e di cifre c h e non
s o n o im m e d ia ta m e n te successive a u n a le tte ra :

grep [''A-Za-z][0-9]+ Homework.java

La n o ta z io n e ['"A-Za-z] in d ic a “ qualsiasi c a ra tte re c h e non è c o m p re s o n e ll’in te rv a llo fra A


e Z, n é fra a e z” . Il risu lta to è m o lto m ig lio re e m o stra so lta n to le rig h e c h e c o n te n g o n o
e ffe ttiv a m e n te n u m e ri.
Il m e to d o useDelimiter d ella classe Scanner a c c e tta u n ’e s p re ssio n e c a n o n ic a p e r
d e sc riv e re i d e lim ita to ri, c io è i b lo c c h i di te sto c h e se p a ra n o le “ p a ro le ” . C o m e già v isto
in p re c e d e n z a , u sa n d o ['"A-Za-z] c o m e sc h e m a , il d e lim ita to re risu lta d e fin ito c o m e u n a
se q u e n z a di u n o o p iù c a ra tte ri c h e n o n sia n o le tte re .
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 591

La classe S tring h a d u e u tili m e to d i c h e u sa n o esp ressio n i c a n o n ic h e . Il m e to d o s p l i t


su d d iv id e u n a strin g a in u n a rray di strin g h e , u sa n d o c o m e d e lim ita to ri se q u e n z e di
c a ra tte ri d e fin ite da u n ’e sp re ssio n e c a n o n ic a . A d e s e m p io

String[] tokens = line.split("\\s+");

s c o m p o n e line u sa n d o i c a ra tte ri di sp a zia tu ra c o m e d e lim ita to ri. Il m e to d o replaceAll


restitu isc e u n a strin g a in c u i tu tte le o c c o r re n z e di u n ’e sp re ssio n e c a n o n ic a s o n o state
so stitu ite da u n a s trin g a d a ta .A d ese m p io , la strin g a re stitu ita da word.replaceAll("[aeiou]",
"") è u g u a le a word, da c u i so n o sta te rim o sse tu tte le v o cali.
P er avere m a g g io ri in f o rm a z io n i in m e rito alle esp re ssio n i c a n o n ic h e c o n s u lta te u n o
d ei ta n ti siti I n te r n e t c h e n e p a rla n o , u sa n d o ad e s e m p io c o m e ch iav e di ric e rc a “ re g u la r
ex p re ssio n tu to r ia l” .

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)));

11.3 Argomenti sulla riga dei comandi


In re la z io n e al siste m a o p e r a tiv o e al siste m a d i s v ilu p p o d i Jav a u tiliz z a ti, e s is to n o
m e to d i d iv e rsi p e r e s e g u ire u n p ro g ra m m a : s e le z io n a n d o “ R u n ” n e l l’a m b ie n te d i
c o m p ila z io n e , a ttiv a n d o u n ’ic o n a o d ig ita n d o il n o m e d e l p r o g ra m m a in u n a fin e stra
d i c o m a n d i. Q u e s t ’u ltim o m e to d o è d e tto “ in v o c a z io n e d e l p r o g ra m m a dalla rig a
d e i c o m a n d i” : q u a n d o u sa te q u e s to m e to d o d o v e te n a tu r a lm e n te d ig ita re il n o m e
d e l p r o g ra m m a , m a p o te te a n c h e a g g iu n g e r e a ltre in f o r m a z io n i c h e il p r o g ra m m a
p o tr e b b e u tiliz z a re . Q u e s t e s tr in g h e a d d iz io n a li s o n o d e tte argomenti sulla riga
dei comandi {command line arguments). A d e s e m p io , se m e tte te in e s e c u z io n e u n
p r o g ra m m a in q u e s to m o d o :

java ProgramClass -v input.dat

allo ra il p ro g ra m m a ProgramClass ric e v e d u e a rg o m e n ti dalla rig a di c o m a n d o : le s trin g h e


"-v" e "input.dat". C o sa fare c o n q u e s te s trin g h e è u n a d e c isio n e c h e c o m p e te in te r a ­
m e n te al p ro g ra m m a , a n c h e se di so lito le s trin g h e c h e in iz ia n o c o n u n tra ttin o v e n g o n o
in te r p re ta te c o m e o p z io n i.
È b e n e c h e i vostri p ro g ra m m i c o n s e n ta n o !’u tilizz o di a rg o m e n ti sulla rig a di c o m a n d o
o p p u re è m e g lio c h e c h ie d a n o tali in fo rm a z io n i all’u te n te , m ag ari c o n u n a interfaccia grafica?
P e r u n u te n te o cc asio n ale u n ’in te rfa cc ia grafica è d e c isa m e n te m ig lio re, p e rc h é lo g u id a
592 C a p it o l o 11

e re n d e possibile !’u tilizzo d e ll’a p p lica zio n e senza b iso g n o di m o lte in fo rm a z io n i, m a p e r


u n u te n te co n so lid a to , c h e usa il p ro g ra m m a m o lto spesso, l’in te rfa cc ia a riga di c o m a n d o
ha u n g ra n d e v an tag g io : si p u ò fa c ilm e n te a u to m atizz are. D o v e n d o elab o rare ce n tin a ia di
file o g n i g io rn o , p o tre ste tra sc o rre re g ra n p a rte del v o stro te m p o a d ig itare i lo ro n o m i
all’in te r n o di u n a fin estra grafica di dialo g o : u san d o , invece, u n file batch o u n o shell script
(una caratte ristic a m essa a d isp o sizio n e dai sistem i o p erativ i) p o te te m e tte re in ese c u z io n e
a u to m a tic a m e n te u n p ro g ra m m a m o lte v olte, c o n a rg o m e n ti diversi sulla riga di c o m a n d o .
I programmi che vengono eseguiti G li a rg o m e n ti fo rn iti nella rig a di c o m a n d o g iu n g o n o al p ro g ra m m a all’in te r n o del
dalla riga di comando ricevono p a ra m e tro args d el m e to d o main.
i relativi argomenti
nel metodo m ain . public static void main(String[] args)

N e l n o stro e s e m p io args è u n array di lu n g h e z z a 2 e c o n tie n e q u e s te strin g h e :

args[0]: "-v "


args[l]: "input.dat"

V ed ia m o c o m e sc riv e re u n p ro g ra m m a c h e cifra u n file, c io è c h e n e m o d ific a il c o n te n u to


in m o d o c h e risu lti illeg g ib ile a c h iu n q u e , tr a n n e a ch i c o n o s c e il m e to d o di d e c ifra z io n e .
Ig n o ra n d o d u e m ila a n n i di p ro g re ssi n e l c a m p o d ella cifra tu ra , u se re m o il m e to d o di
G iu lio C e sare , c h e p rev e d ev a la so stitu z io n e d i A c o n D, B c o n E , e così via (F ig u ra 1).

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 :

• U n flag o p z io n a le , -d, p e r se g n ala re la ric h ie sta di d e c ifra z io n e a n z ic h é c ifratu ra .


• Il n o m e d el file d a leg g ere.
• Il n o m e d el file d a scriv ere.

A d ese m p io :

java CaesarCipher input.txt encrypt.txt

cifra il file input.txt, m e m o r iz z a n d o il ris u lta to n el file encrypt.txt, m e n tre :

java CaesarCipher -d encrypt.txt output.txt

d ec ifra il file encrypt.txt, m e m o r iz z a n d o il risu lta to n el file output.txt.

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 ?

java CaesarCipher -kl5 input.txt output.txt

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 .

Problema. L e g g e r e d u e f ile d i d a ti n a z i o n a l i, worldpop.txt e worldarea.txt ( p r e s e n t i n e l


p a c c h e t t o d e i f ile s c a r ic a b ili p e r q u e s t o lib r o ): il p r i m o c o n t i e n e d a ti su lla p o p o l a z i o n e ,
n a z i o n e p e r n a z i o n e , m e n t r e n e l s e c o n d o si t r o v a n o i d a ti su lla s u p e r f ic ie , in c h i l o m e t r i
q u a d ra ti; le n a z io n i s o n o e le n c a te n e llo stesso o r d in e n e i d u e file. S criv ere il file world_
pop_density.txt c h e c o n te n g a i n o m i d elle n a z io n i e le relativ e d e n sità di p o p o la z io n e
(ab ita n ti p e r c h ilo m e tro q u a d ra to ), c o n i n o m i d elle n a z io n i in c o lo n n a ti a sin istra e i
n u m e r i in c o lo n n a ti a destra:

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 o m e sem p re, p rim a di p ro g e tta re u n a so lu z io n e p e r u n p ro b le m a b iso g n a averlo c o m p re so


c h ia ra m e n te . S ie te in g ra d o di riso lv e re il p ro b le m a a m a n o (e v e n tu a lm e n te c o n file di
d ati di m in o r i d im e n sio n i)? Se n o n riu sc ite a farlo, d o v e te ac q u isire u lte r io ri in fo rm a z io n i
in re la z io n e al p ro b le m a .
U n a sp e tto im p o r ta n te c h e d o v e te c o n s id e ra re è se sia p o ssib ile e la b o ra re i d ati n el
m o m e n to in c u i q u esti d iv e n ta n o d isp o n ib ili o p p u re se li d o v e te p rim a m e m o riz z a re tu tti.
A d ese m p io , se vi v ie n e c h ie sto di v isu alizzare i d ati s e c o n d o u n d e te r m in a to c r ite r io
di o rd in a m e n to , d o v e te p rim a ac q u isirli tu tti, p r o b a b ilm e n te in se re n d o li in u n v e tto re .
S pesso, in v ece, i d ati p o s s o n o essere e la b o ra ti “ al v o lo ” {on the go), sen za m e m o riz z a rli.
N e l n o stro e s e m p io p o ssia m o le g g e re da c iasc u n file u n a rig a alla v o lta e calc o lare la
d en sità di p o p o la z io n e p e r q u e lla n a z io n e , p e rc h é i file d i d ati c o n te n g o n o la p o p o la z io n e
e la su p e rfic ie n e llo stesso o rd in e .
L o p s e u d o c o d ic e s e g u e n te d e sc riv e l’e la b o ra z io n e da c o m p ie re .

Finché ci sono altre righe da leggere


Leggi una riga da ciascun file.
Estrai il nome della nazione.
popolazione = numero che segue la nazione nella riga del primo file
superficie = numero che segue la nazione nella riga del secondo file
Se superficie != 0
densità = popolazione / superficie
Scrivi nel file il nome della nazione e la densità.

Fase 2 In d iv id u a te i file da le g g e re e q u e lli da scriv ere.

Q u e s to d o v re b b e essere b e n c h ia ro dalla d e s c riz io n e d el p ro b le m a . N e l n o stro e s e m p io


ci so n o d u e file da le g g e re (i d ati sulla p o p o la z io n e e q u e lli sulla su p e rfic ie) e u n file da
scriv e re (c o n le d en sità ).

Fase 3 S ceg lie te u n m e c c a n is m o p e r a c q u isire i n o m i d e i file.

C i so n o tre o p z io n i:

• C o d ific a re i n o m i d e i file aH’in te r n o d el p ro g ra m m a (c o m e "worldpop.txt"):


• C h ie d e re aH’u te n te :
596 C a p it o l o 11

Scanner in = new Scanner(System.in);


System.out.print("Enter filename: ");
String inFile = in.nextLine();

U sa re a rg o m e n ti sulla rig a di c o m a n d o .

N e l n o stro e se m p io , p e r se m p lic ità , a b b ia m o u sa to la p rim a o p z io n e .

Fase 4 D e c id e te se ac q u isire i d ati p e r rig h e , p e r p a ro le o p e r sin g o li c a ra tte ri.

C o m e reg o la e m p iric a , a c q u isite rig h e q u a n d o i d ati so n o r a g g ru p p a ti p e r rig h e : q u e s to


a v v ie n e se, c o m e n e l n o stro ese m p io , i d ati v e n g o n o fo rn iti in fo rm a to ta b u lare. Q u e s ta
stra te g ia è u tile a n c h e q u a n d o è n e c e ssa rio fare rife r im e n to al n u m e ro di rig a.
Q u a n d o si a c q u is is c o n o d a ti c h e s o n o d is trib u iti su p iù rig h e , è p iù s e m p lic e le g ­
g e re u n a p a ro la alla v o lta . In q u e s to caso o c c o r r e r ic o r d a r e c h e si p e r d o n o i c a ra tte ri
di sp a z ia tu ra .
L’a c q u isiz io n e d ei sin g o li c a ra tte ri è u tile s o p r a ttu tto q u a n d o il p ro b le m a ric h ie d e ,
a p p u n to , l’accesso ai singoli c a ra tte ri: tra gli e sem p i, p o ssiam o citare l’analisi d ella fre q u en z a
d ei c a ra tte ri in u n testo , la cifra tu ra o la tra s fo rm a z io n e d e i c a ra tte ri di ta b u la z io n e in
spazi.

Fase 5 Q u a n d o i d ati s o n o o rg a n iz z a ti p e r rig h e , e stra e te i d ati ric h ie sti.

C o n il m e to d o nextLine è m o lto se m p lic e le g g e re u n a rig a d i d ati in in g resso : p o i, b iso g n a


e stra rre i d ati d a ciasc u n a rig a , ad e s e m p io e s tra e n d o s o tto s trin g h e , c o m e d e s c ritto n el
P arag rafo 1 1 .2 .4 .
S o lita m e n te i c o n fin i d elle s o tto s trin g h e v e rr a n n o in d iv id u a ti u sa n d o m e to d i c o m e
Character.isWhiteSpace o Character.isDigit.

Fase 6 U sa te classi e m e to d i p e r realizzare i c o m p iti m a g g io r m e n te r ip e tu ti.

L’a c q u isiz io n e di d ati da file p re v e d e so lita m e n te a lc u n i c o m p iti rip e titiv i, c o m e il fa tto di


ig n o ra re i c a ra tte ri di sp a zia tu ra o di e stra rre n u m e ri da s trin g h e .T ra tta n d o si di o p e ra z io n i
m o lto n o io se , è d a v v e ro u tile isolarle dal resto d el c o d ic e .
N e l n o s tro e s e m p io , n o tia m o u n c o m p ito c h e r ic o r r e d u e v o lte : s u d d iv id e re u n a
rig a p e r o tte n e r e il n o m e d i u n a n a z io n e e il v a lo re c h e lo se g u e . P e r riso lv e re q u e s to
p ro b le m a re a liz z ia m o la se m p lic e classe CountryValue, u s a n d o la te c n ic a d e s c ritta n e l
P a ra g ra fo 1 1 .2 .4 ..
E c c o , in fin e , il p ro g ra m m a c o m p le to .

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

10 Costruisce un CountryValue a partire da una riga.


11 @param line riga che contiene il nome di una nazione, seguito da un valore
12 */
13 public CountryValue(String line)
14 {
15 int i = 0; // trova la prima cifra
16 while ( !Character.isDigit(line.charAt(i))) { i++; }
17 int j = i - 1; // trova la fine della parola precedente
18 while (Character.isWhitespace(line.charAt(j))) { j— ; }
19 country = line.substring(o, j + l); // estrae il nome della nazione
20 value = Double.parseDouble(line.substring(i).trim()); // estrae il valore
21 }
22
23 /**
24 Restituisce il nome della nazione.
25 @return il nome della nazione
26 */
27 public String getCountryO { return country; }
28
29 /**
30 Restituisce il valore associato.
31 ^return il valore associato alla nazione
32 */
33 public double getValue() { return value; }
34 }

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

out.printf("%-40s%l5.2f\n", population.getCountryO, density);


}

inl.closeO;
in2.close();
out.closeO;

Esempi completi 11.1 ..............................


ù Analizzare i nomi dei bambini
N e g li S tati U n iti, la S ocial S e c u rity A d m in is tra tio n p u b b lic a , sul p r o p rio sito w e b ,g li e le n ­
c h i d ei n o m i p iù diffusi tra q u e lli a ttrib u iti ai n u o v i n ati: http://www.ssa.gov/OACT/babynames/.
C h ie d e n d o i 1 0 0 0 n o m i p iù diffusi in u n d e te r m in a to d e c e n n io , il b ro w s e r visualizza il
risu lta to sullo s c h e rm o , e s a tta m e n te c o m e si p u ò v e d e re n ella fig u ra q u i r ip o rta ta .

Edit y&ew Hiitory fiookmarfc$ B>ots fcjelp

^ 0 O A IS httpV^l^ww.t8^.9oy>OACT/b^byn^m^«/d^¢^d^«A^

Soc-ISecuhtyOnUne PopulaF Names


www.tociateecuriy4lov Homo Question»? * Contact Us - | Search i
P q p g te fg tQ yN a m cs Popular Baby Names By Decade

Most Popular 1000 Names of the 1990s


Select another
decade? All names are from Social Security card appicadons for births that
Decade C Go occurred in the United States The data below were e)(tracted from our
records at the end of February 2000 See limitations of such data The
most popular 1000 names of the 1990s were taken from a universe
Nymfeflf-fil&inh? that includes 20,531.547 male births and 19.627.269 female births

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

P er m e m o riz z a re q u e sti d ati in fo rm a di testo , basta s e m p lic e m e n te se lez io n arli e, c o n u n a


p ro c e d u ra di “ c o p ia e in c o lla ” , in se rirli in u n file, c o m e il file babynames.txt relativ o ai n o m i
u tilizz ati n eg li A n n i N o v a n ta e re p e rib ile n el p a c c h e tto d ei file scarica b ili p e r q u e s to libro.
O g n i rig a d e l file c o n tie n e se tte d ati:
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 599

• 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 :

10 Joseph 260365 1.2681 Megan 160312 0.8168

m o stra c o m e il d e c im o n o m e m a sch ile p iù d iffu so sia stato, in q u el d e c e n n io , Jo s e p h ,


u sa to p e r u n a p e rc e n tu a le di b a m b in i p a ri a 1 .2 6 8 1 % , c io è p e r 2 6 0 3 6 5 b a m b in i, m e n tre
il d e c im o n o m e fe m m in ile p iù diffuso è stato M e g a n . P e rc h é J o s e p h è p iù diffuso di
M e g a n ? S e m b ra c h e i g e n ito r i u sin o u n in sie m e di n o m i fe m m in ili p iù a m p io , fa c e n d o
così in m o d o c h e c ia sc u n o di essi sia m e n o fre q u e n te .
11 v o stro c o m p ito è q u e llo di v e rific a re q u esta s u p p o siz io n e , d e te r m in a n d o T in sie m e
d ei n o m i a ttrib u iti al 5 0 p e r c e n to d ei b a m b in i e al 5 0 p e r c e n to d elle b a m b in e p iù in
alto nella lista. S e m p lic e m e n te , d o v e te v isu alizzare i n o m i di b a m b in i e b a m b in e , in sie m e
alla lo ro p o s iz io n e in classifica, fin c h é n o n avete ra g g iu n to , s e p a ra ta m e n te p e r c ia sc u n o
d ei d u e sessi, il 5 0 p e r c e n to di u tiliz z o co m p lessiv o .

Fase 1 C a p ite il p ro b le m a .

P er e la b o ra re ciasc u n a rig a d el file, p e r p r im a cosa le g g ia m o la p o siz io n e , q u in d i le g g ia m o


tre v a lo ri p e r il n o m e m a sc h ile (n o m e , n u m e ro di u tilizz i e p e rc e n tu a le ), in fin e rip e tia ­
m o la p ro c e d u ra p e r il n o m e fe m m in ile . P e r p o te r in te r ro m p e re la p ro c e d u ra q u a n d o si
ra g g iu n g e il 50 p e r c e n to , s o m m ia m o le p e rc e n tu a li e ci fe rm ia m o q u a n d o , a p p u n to , tale
so m m a ra g g iu n g e il 50 p e r c e n to .
C i se rv o n o d u e s o m m e se p arate p e r m a sch i e fe m m in e . Q u a n d o u n a d elle d u e
ra g g iu n g e il 50 p e r c e n to , s m e ttia m o di v isu alizzare in fo rm a z io n i p e r q u e l sesso; q u a n d o
e n tra m b e r a g g iu n g o n o il 5 0 p e r c e n to , s m e ttia m o di le g g e re d ati.
L o p s e u d o c o d ic e se g u e n te d e sc riv e il p ro ce sso di e la b o ra z io n e c h e a b b ia m o p r o g e t­
tato.

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.

Fase 2 In d iv id u a te i file da le g g e re e q u e lli da scriv ere.

C ’è u n so lo file da le g g ere: babynames.txt. N o n ci è sta to c h ie sto di sc riv e re i risu lta ti in


u n file, q u in d i v isu a liz z e re m o le in fo rm a z io n i u sa n d o System.out.
600 C a p it o l o 11

Fase 3 S c e g lie te u n m e c c a n is m o p e r a c q u isire i n o m i d ei file.

N o n a b b ia m o b iso g n o di c h ie d e re all’u te n te il n o m e d el file.

Fase 4 D e c id e te se a c q u isire i d ati p e r rig h e , p e r p a ro le o p e r sin g o li c a ra tte ri.

I d ati in esam e n o n c o n te n g o n o n o m i c o n spazi, c o m e “ M a ry J a n e ” , q u in d i o g n i rig a di


d ati c o n tie n e e s a tta m e n te se tte e n tità . D a ti d i q u e s to tip o p o sso n o essere ac q u isiti sen za
p ro b le m i e la b o ra n d o p a ro le e n u m e ri.

Fase 5 Q u a n d o i d ati s o n o o rg a n iz z a ti p e r rig h e , e stra e te i d a ti ric h ie sti.

P o ssiam o ig n o ra re q u e s ta fase, p e rc h é n o n le g g e re m o i d ati a c q u is e n d o u n a rig a alla v o lta.


Se, p e rò , nella fase p r e c e d e n te avessim o d ec iso di a c q u isire i d ati u n a rig a alla v o lta ,
d o v re m m o s c o m p o rre la rig a ac q u isita in se tte strin g h e , c o n v e r te n d o n e c in q u e in n u m e ri:
u n a p r o c e d u ra a lq u a n to n o io sa , c h e d o v re b b e farvi c a m b ia re id ea.

Fase 6 U sa te classi e m e to d i p e r realizzare i c o m p iti m a g g io r m e n te r ip e tu ti.

N e llo p s e u d o c o d ic e a b b ia m o s c ritto Ripeti per il sesso femminile, q u in d i è fa c ilm e n te in d i­


v id u a b ile u n c o m p ito rip e tu to , c h e su g g e risc e la d e fin iz io n e di u n m e to d o au siliario ,
c o s titu ito da tre az io n i:

Leggi il nome, il suo conteggio e la sua percentuale.


Visualizza il nome se totale < 50.
Aggiungi la percentuale al totale.

P er riso lv e re q u e s to p ro b le m a u sia m o u n a classe au siliaria, RecordReader, e n e c o s tru ia m o


d u e o g g e tti, u n o p e r e la b o ra re i n o m i m a sch ili e u n o p e r i n o m i fe m m in ili. O g n i o g g e tto
RecordReader g estisce u n to ta le d is tin to e lo a g g io rn a a g g iu n g e n d o v i la n u o v a p e rc e n tu a le ,
v isu a liz z a n d o i n o m i fin o al r a g g iu n g im e n to d el lim ite p rev isto . Il n o stro c ic lo di e la b o ­
ra z io n e p rin c ip a le d iv e n ta q u in d i:

ReeordReader boys = new ReeordReader(LIMIT); // masehi


ReeordReader girls = new ReeordReader(LIMIT); // femmine
while (boys.hasMoreO || girls.hasMore())
{
int rank = in.nextlnt();
System.out.print(rank + " ");
boys.proeess(in);
girls.proeess(in);
System.out.println();
}

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

string name = in.next();


int count = in.nextIntO;
double percent = in.nextDouble();

if (total < limit) { System.out.print(name + " "); }


total = total + percent;
}

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;

public class BabyNames


{
public static final double LIMIT = 50;

public static void main(String[] args) throws FileNotFoundException


{
try (Scanner in = new Scanner(new File("babynames.txt")))
{
RecordReader boys = new RecordReader(LIMIT);
RecordReader girls = new RecordReader(LIMIT);

while (boys.hasMoreO || girls.hasMore())


{
int rank = in.nextInt();
System.out.print(rank + " ");
boys.process(in);
girls.process(in);
System.out.println();
}

File RecordReader.java
import java.util.Scanner;

Questa classe elabora dati sui nomi dei bambini.


*/
public class RecordReader
{
private double total;
private double limit;
602 C a p it o l o 11

/**
Costruisce un RecordReader con totale uguale a zero.
*/
public RecordReader(double aLimit)
{
total = 0;
limit = aLimit;
}

Legge una riga e visualizza il nome


se il totale è inferiore al limite.
@param in il flusso di ingresso
*/
public void process(Scanner in)
{
String name = in.next();
int count = in.nextlntO;
double percent = in.nextDouble();

if (total < limit) { System.out.print(name + " "); }


total = total + percent;
}

Verifica se ci sono altri dati da elaborare,


^return true se il limite non è stato raggiunto
*/
public boolean hasMore()
{
return total < limit;
}

Computer e S Q d età 11,1

Algoritmi di cifratura in iziali d ei c o g n o m i d ei su o i in v e n ­ O n ella firm a d e i m essag g i di p o sta


G li e s e rc iz i p re s e n ta ti alla fin e d i to ri). L o sc h e m a c o m p le to è tro p p o e le ttro n ic a .A q u e sto p u n to , c h iu n q u e
q u e s to c a p ito l o r i p o r t a n o a lc u n i c o m p lic a to p e r p o te r lo d e s c riv e re v o g lia s p e d irv i u n m e s s a g g io c h e
a lg o ritm i p e r cifrare te sti, m a n o n q u i, m a n o n è p o i così d ifficile da possa essere d e c ifra to so lta n to da v o i,
usateli p e r m a n d a re m e ssag g i se g re ti se g u ire , c o n s u lta n d o n e i d e tta g li a lo p u ò cifrare c o n la v o stra ch iav e
alla p e rs o n a c h e a m a te : q u a lu n q u e q u e s to in d iriz z o : http://theory.lcs. p u b b lic a . A n c h e se tu tti c o n o s c o n o
c r it to g r a f o e s p e r to è in g r a d o in m i t .edu/~rivest/rsapaper.pdf. la v o stra ch iav e p u b b lic a e a n c h e se
b rev e te m p o di violare q u e g li sc h e m i, R S A è u n m e to d o di cifra tu ra q u a lc u n o riu scisse a in te r c e tta r e il
c io è di r ic o s tru ire il te sto o r ig in a le v e ra m e n te in te ressa n te. C o m e si p u ò m e ssag g io c ifrato d ire tto a v oi, n o n
sen za c o n o s c e re la ch ia v e se g re ta . v e d e re nella fig u ra , usa d u e ch iav i: sa re b b e in g ra d o di d e c ifra rlo e di
N e l 1978, R o n R iv e s t,A d i S h a ­ u n a p u b b lic a (public key) e u n a p r i­ leggerlo. N e l 1 9 94, ce n tin a ia di r ic e r­
m ir e L eo n a rd A d lem an p re se n ta ro n o vata (private key). La ch iav e p u b b lic a c a to ri, c o lla b o ra n d o attrav erso le re te
u n m e to d o di cifratu ra d e c isa m e n te p u ò essere d istrib u ita a c h iu n q u e , ad I n te r n e t, fu ro n o in g ra d o di v io la re
p iù p o te n te , c h ia m a to RSA (dalle e sem p io sul p ro p rio b ig lie tto da visita u n m e ssag g io R S A c ifrato c o n u n a
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 603

c h i a v e a 129 c if r e , m a i m e s s a g g i sc o p e rte da u n a u m e n to d ella r e p u ­ c e ” ), c h e è b asato su R S A : c h iu n q u e


o d i e r n i , c h e u s a n o c h ia v i a 2 3 0 c ifr e ta z io n e e da a v a n z a m e n ti di c a rrie ra . p u ò usare il p ro g ra m m a p e r in v iare
O p i ù , s o n o r it e n u t i s ic u r i. M a ci sa re b b e stata a ltre tta n ta attiv ità m essaggi cifrati e la lo ro d ec ifra zio n e,
G li in v e n to ri di q u e s to a lg o rit­ nella m essa a p u n to d i m ig lio ra m e n ti in m a n c a n z a d ella ch iav e o p p o r tu n a ,
m o o tte n n e r o su di esso u n brevetto, di q u e ll’a lg o ritm o , c o n u lte r io ri b re ­ n o n è p ra tic a b ile n e m m e n o c o n i
c h e è u n c o n tr a tto stip u la to tra la v etti, c o m e in effetti c ’è stata? Q u e s to c o m p u te r p iù p o te n ti. Il p ro g ra m m a
so c ietà e u n in v e n to re : p e r u n p e ­ n o n si p u ò sapere, o v v ia m e n te . In o l­ si p u ò o tte n e r e g ra tu ita m e n te e fa
r io d o di 2 0 a n n i, l ’in v e n to re h a il tre, è ra g io n e v o le b re v e tta re u n a lg o ­ p a rte d el p ro g e tto G N U (http://www.
d ir itto esclusivo di co m m e rc ia liz z a re r itm o o p p u re si tra tta di u n ’ev id e n z a gnupg.org). L’esiste n za di m e to d i di
l’in v e n z io n e , p u ò incassare i d iritti m a te m a tic a , c h e n o n è p a trim o n io di c ifra tu ra m o lto ro b u sti p re o c c u p a da
(royalties) p ag a ti da altri c h e v o g lia n o n essu n o ? P e r lu n g o te m p o l’u ffic io se m p re il g o v e rn o d eg li S tati U n iti:
u tilizz are l’in v e n z io n e e p u ò a d d i­ b re v e tti h a se g u ito q u e sta strad a e, i c rim in a li e gli ag e n ti stra n ie ri so n o
r i ttu r a i m p e d ir n e c o m p le t a m e n te in f a tti, gli in v e n to r i d i R S A e d i in g ra d o di scam biarsi c o m u n ic a z io n i
!’u tiliz z o a e v e n tu a li c o n c o r r e n ti. m o lti altri a lg o ritm i h a n n o d e s c ritto c h e la p o liz ia e i serv iz i se g re ti n o n
C o m e c o n tr o p a r tita , l’in v e n to r e è le lo ro in v e n z io n i in te rm in i di u n p o sso n o d ecifrare. Il g o v e rn o h a te n ­
te n u to a re n d e re p u b b lic a l ’in v e n ­ im m a g in a rio d isp o sitiv o e le ttro n ic o , ta to di in c rim in a re Z im m e r m a n n p e r
z io n e , in m o d o c h e altri la p o ssa n o p iu tto s to c h e di u n a lg o ritm o , a g g i­ aver in fra n to u n a le g g e c h e p ro ib isc e
stu d iare, p e r d e n d o o g n i d ir itto su di ra n d o così q u e lla re striz io n e . O g g i, l ’e s p o r ta z i o n e n o n a u to r iz z a ta di
essa al te rm in e del p e r io d o di m o ­ in v e ce , l’u ffic io b re v e tti a m m e tte i a rm i, s o ste n e n d o c h e a v re b b e d o v u to
n o p o lio . Q u e s to m e c c a n is m o si basa b re v e tti so ftw are. sa p ere c h e il su o p ro g ra m m a sa re b b e
su ll’ip o te si ch e , in m a n c a n z a di u n a C ’è a n c h e u n altro a sp e tto rile ­ sta to diffuso tra m ite In te r n e t. In o ltre
leg g e sui b re v e tti, gli in v e n to ri n o n v a n te nella s to ria di R S A . U n p r o ­ è sta to p ro p o s to di r e n d e re illegale,
s a re b b e ro in c e n tiv a ti a s v ilu p p a re g ra m m a to re , P h il Z im m e r m a n n , ha p e r i p riv a ti c itta d in i, l’u so di q u esti
nuove id e e o p p u re c e rc h e re b b e ro di sv ilu p p a to u n p ro g ra m m a , c h ia m a to m e to d i di cifratu ra o, q u a n to m e n o , di
te n e re nasco ste le te c n ic h e in n o v a ti­ P G P (ch e sta p e r Pretty Good Privacy, m a n te n e re se g re te le p r o p rie ch iav i
ve p e r im p e d ire ad altri di c o p ia re i c io è “ rise rv a te z z a p iu tto s to effica­ a n c h e di fro n te alla legge.
p r o p ri d isp o sitiv i.
In m e r i to al b r e v e tto d e l l’a l-
g o r itm o R S A è s o rta q u a lc h e d i­
s p u ta . In p a r tic o la r e , ci si c h ie s e :
in m a n c a n z a di u n a p r o te z io n e d el
b re v e tto , i su o i in v e n to r i a v re b b e ro
c o m u n q u e p u b b lic a t o il m e to d o ,
c o n s e g n a n d o n e co sì i b e n e fic i alla
so c ie tà sen za c h e q u e s ta d o v esse in
q u a lc h e m o d o p a g a re il c o s to di u n
m o n o p o li o v e n te n n a le ? In q u e s to
caso, la ris p o s ta è “ p r o b a b ilm e n te
sì” , p e r c h é gli in v e n to r i e r a n o r i ­
c e rc a to ri a c c a d e m ic i, c h e v iv o n o d el
p r o p rio stip e n d io p iu tto s to c h e d elle
v e n d ite di p r o d o tti e, s o lita m e n te ,
s o n o r ic o m p e n s a ti p e r le p r o p r ie
604 C a p it o l o 11

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 .

11.4.1 Lanciare eccezioni


Per segnalare una condizione ] Q u a n d o in d iv id u a te u n a c o n d iz io n e d ’e r r o re , il v o s tro c o m p ito è v e r a m e n te se m p lic e :
Kcezionale si usa l'enunciato th ro w j l a n c i a t e ( t h r o w ) u n o g g e tto a p p r o p r ia t o di tip o e c c e z io n e e n o n d o v e te fa re a ltro . A d
Ianciandounoggetto e s e m p io , s u p p o n e te c h e q u a lc u n o c e rc h i di p re le v a re tro p p i so ld i da u n c o n to b a n c a rio .
di tipo "eccezione". I
if (amount > balance)
{
// cosa facciamo ora?
}

P er p rim a cosa c e rc h ia m o u n a classe di e c c e z io n i c h e sia a d e g u a ta . La lib re ria Java m e tte


a d isp o s iz io n e m o lte classi p e r seg n alare v ari tip i d i c o n d iz io n i ec c e z io n a li: la F ig u ra 2
m o stra q u e lle p iù u tili (le classi s o n o o rg a n iz z a te s e c o n d o u n a g e ra rc h ia di e re d ita rie tà
ad alb ero , c o n le classi p iù sp e c ific h e nella z o n a p iù bassa).
C e rc a te u n ’e c c e z io n e c h e p o tr e b b e d e sc riv e re la situ a z io n e in esam e. C h e n e d ite
di AritmeticException? A rriv a re ad avere u n saldo n e g a tiv o è u n e rro re a ritm e tic o ? N o n
è così, p e rc h é Java è a s so lu ta m e n te in g ra d o di e la b o ra re n u m e ri n eg a tiv i. È la q u a n tità
di d e n a ro da p re le v a re a n o n essere valida? C e rto , h a u n v alo re tr o p p o elev ato , q u in d i
la n c ia m o u n o g g e tto di tip o IllegalArgumentException.

if (amount > balance)


{
throw new IllegalArgumentException("Amount exceeds balance");
}

Quando si lancia un'eccezione, Q u a n d o l a n c i a t e u n ’ e c c e z i o n e ( t h r o w a n e x c e p t i o n ) , l’e s e c u z io n e n o n p ro c e d e c o n


l'elaborazione continua in un gestore l’e n u n c ia to su ccessivo m a passa a u n g e s t o r e d e l l ’e c c e z i o n e ( e x c e p t i o n h a n d l e r ) , di cu i
dell'eccezione. p a rle re m o n el p a ra g ra fo successivo.

11.4.2 Catturare eccezioni


Inserite all'interno di un blocco t r y T u tte le e c c e z io n i d o v re b b e ro essere g e stite in q u a lc h e p u n to d el v o stro p ro g ra m m a . Se
gli enunciati che possono lanciare u n ’e c c e z io n e n o n ha u n g e sto re e v ie n e lan cia ta , v ie n e v isu a liz za to u n m e ssag g io d ’e rro re
un'eccezione, scrivendo poi il gestore e il p ro g ra m m a te rm in a , q u in d i u n ’e c c e z io n e n o n g estita p u ò c e r ta m e n te c o n fo n d e re
in una clausola c a tc h . l’u te n te d el p ro g ra m m a .
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 605

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

Scanner in = new Scanner(new File(filename));


String input = in.next();
int value = Integer.parselnt(input);

} * ’ ’
catch (IOException exception)
{
exception.printStackTrace();
}
catch (NumberFormatException exception)
{
System.out.println(exception.getMessage());
}

In q u e s to b lo c c o try p o sso n o essere la n c ia te e c c e z io n i di tre tip i diversi.

• Il c o s tru tto r e di Scanner p u ò la n cia re FileNotFoundException.


• Il m e t o d o next di Scanner p u ò lanciare NoSuchElementException.
• Il m e to d o Integer.parseint p u ò la n cia re NumberFormatException.

Se u n a d i q u e s te e c c e z io n i v ie n e e ffe ttiv a m e n te la n cia ta , i r im a n e n ti e n u n c ia ti d el b lo c c o


tr y n o n v e n g o n o e se g u iti. E c c o c iò c h e a c c a d e p e r i d iv ersi tip i di e c c e z io n i:

• Se v ie n e lan ciata FileNotFoundException, v ie n e eseg u ita la clausola catch c o rr is p o n d e n te


a I O E x c e p t i o n ( g u a r d a n d o la F ig u ra 2 , n o t e r e t e c h e F i l e N o t F o u n d E x c e p t i o n è u n a
so tto classe d i lOException). Se v o le te c h e il m e ssag g io v isu a liz za to all’u te n te in se g u ito
al v erific arsi di u n a FileNotFoundException sia diverso, d o v e te in se rire u n a clausola catch
sp ecifica p e r tale e c c e z io n e prima della clau so la catch c h e g estisce lOException.
• Se v ie n e la n cia ta NumberFormatException, v ie n e ese g u ita la se c o n d a clausola catch.
• U n ’e c c e z io n e di tip o NoSuchElementException non viene catturata da n essu n a delle clausole
catch, p e r c u i l’e c c e z io n e rim a n e “ la n c ia ta ” (cio è attiva) fin c h é n o n v ie n e c a ttu ra ta
da u n altro b lo c c o try .

O g n i clau so la catch c o n tie n e u n g e sto re di e c c e z io n e . Q u a n d o v ie n e e s e g u ito il b lo c c o


d i c o d ic e d ella clau so la catch (lOException exception), sig n ific a c h e u n o d e i m e to d i
in v o c a ti all’in te r n o d el b lo c c o tr y ha la n c ia to u n o g g e tto di tip o lOException (o di u n a
sua so tto classe).
A ll’in te r n o del g e sto re p o te te v isu alizzare u n e le n c o della c a te n a di in v o c a z io n i di
m e to d i c h e h a p o r ta to all’e c c e z io n e , in v o c a n d o

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

throw new IllegalArgumentException("Amount exceeds balance");


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 607

Sintassi di Java 11.1 Lanciare un'eccezione

Sintassi throw oggettoEccezione;

Esempio La maggior parte degli oggetti di tipo


eccezione possono essere costruiti fornendo
if (amount > balance) un messaggio d'errore.
{
Si costruisce un nuovo throw new IllegalArgumentException("Amount exceeds balance");

oggetto di tipo eccezione, }


balance = balance - amount;
poi lo si lancia. Questo enunciato viene eseguito soltanto
se l'eccezione non viene lanciata.

Sintassi di Java 11.2 Catturare eccezioni

Sintassi try
{
enunciato
enunciato

}
catch {ClasseEccezione oggettoEccezione)
{
enunciato
enunciato

Esempio
Questo costruttore può lanciare
try FileNotFoundException.

Scanner in = new Scanner(new File("input.txt"));


String input = in.next();
process(input); Questa è l'eccezione che è stata lanciata.
Quando viene lanciata lOException,
}
l'esecuzione prosegue da questo punto, catch (lOException exception)"

System.out.printlnt*^ould not open input file");


Qui si possono inserire ulteriori }
clausole catch. Le eccezioni più catch (Exception except) FileNotFoundException è un

specifiche vanno scritte prima caso speciale di lOException.


System.o u t.println(except.getMessage());
di quelle più generiche. }
608 C a p it o l o 11

aH’e c c e z io n e lan ciata v ie n e associato il m essag g io fo rn ito c o m e a rg o m e n to n el c o s tru tto re .


N e lle clau so le catch u sate in q u e sto e se m p io ci siam o lim ita ti a in fo rm a re l’u te n te della
causa d el p ro b le m a , m e n tre spesso u n m o d o m ig lio re p e r g estire tali e c c e z io n i co n siste
n e l d are all’u te n te u n ’altra p o ssib ilità di in se rire c o r r e tta m e n te i d ati, c o m e v e d re te n e l
P arag rafo 11.5.

11.4.3 Eccezioni a controllo obbligatorio


I n J a v a le e c c e z io n i ric a d o n o e n tro tre c a te g o rie .

• E rro ri in te rn i segnalati da sottoclassi di Error. U n e se m p io è OutOfMemoryError, u n ’e c c e ­


z io n e c h e v ie n e lanciata q u a n d o la m e m o ria d isp o n ib ile n el ca lc o lato re è stata esaurita.
Si tra tta di e r r o ri fatali c h e a c c a d o n o di ra d o e in q u e s to lib ro n o n n e p a rle re m o .
• Sottoclassi di RuntimeException, c o m e IndexOutOfBoundsException o IllegalArgumentException:
s e g n a la n o u n e r r o r e n e l c o d i c e e s o n o d e t t e a controllo non obbligatorio
{unchecked exception).
• T u tte le altre e c c e z io n i so n o a controllo obbligatorio {checked exception) e seg n alan o
c h e q u a lc o sa è a n d a to s to r to p e r u n m o tiv o e s te r n o al p ro g ra m m a , al d i f u o ri d el
c o n tro llo d e l p r o g ra m m a to re . N e lla F ig u ra 2 le e c c e z io n i a c o n tro llo o b b lig a to r io
so n o ra p p re se n ta te da r iq u a d r i di c o lo re g rig io .

Le eccezioni a controllo obbligatorio P e rc h é ci so n o d u e tip i di e c c e z io n i? U n ’e c c e z io n e a c o n tro llo o b b lig a to rio d e sc riv e u n


sono dovute a circostanze esterne p ro b le m a c h e p rim a o p o i p u ò ac ca d ere, in d ip e n d e n te m e n te d a q u a n to il p r o g ra m m a to re
■ il programmatore non può evitare sia a tte n to . P er ese m p io , u n a IOException p u ò essere p ro v o c a ta da f e n o m e n i c h e n o n
e il compilatore verifica r ic a d o n o so tto il c o n tro llo d el p r o g ra m m a to re , c o m e u n e rro re h a rd w a re d el d isc o o
che il programma le gestisca. u n a c o n n e s s io n e di re te in te rro tta . Il c o m p ila to re p re n d e m o lto sul se rio le e c c e z io n i a
c o n tro llo o b b lig a to rio e si a c c e rta c h e v e n g a n o e ffe ttiv a m e n te c a ttu ra te : u n p ro g ra m m a
n o n v e rrà c o m p ila to se n o n sp ecifica c o m e g estire le e c c e z io n i a c o n tro llo o b b lig a to rio
c h e si p o sso n o v e rific a re d u ra n te la sua e se c u z io n e .
L e e c c e z io n i a c o n tro llo n o n o b b lig a to rio , al c o n tra rio , a c c a d o n o p e r u n e rro re
del p ro g ra m m a to re . Il c o m p ila to re n o n v e rific a se e c c e z io n i di q u e sta c a te g o ria , c o m e
IndexOutOfBoundsException, v e n g o n o g estite : in fin d ei c o n ti, in v e ce di sc riv e re u n g e sto re
p e r u n a tale e c c e z io n e , fareste m e g lio a c o n tro lla re i v a lo ri d eg li in d ic i c h e usate.
La d e fin iz io n e di u n g e sto re di u n ’e c c e z io n e a c o n tro llo o b b lig a to rio all’in te r n o d ello
stesso m e to d o c h e p u ò la n cia rla so d d isfa il c o m p ila to re , c o m e in q u e s to ese m p io :

try
{
File inFile = new File (filename);
Scanner in = new Scanner(inFile); // può lanciare FileNotFoundException

}
catch (FileNotFoundException exception) // l'eccezione viene catturata qui
{

Aggiungete la clausola th ro w s Spesso, p e rò , ci si trova in u n a situ a z io n e in c u i u n m e to d o non è in grado di g estire


a un metodo che può lanciare l’e c c e z io n e . In tal caso d o v e te c o m u n ic a re al c o m p ila to re c h e sie te co n sa p e v o li della
un'eccezione a controllo obbligatorio. p ossibilità c h e l’e c c e z io n e v en g a la n ciata e c h e v o le te c h e l’e s e c u z io n e d el m e to d o te rm in i
q u a n d o q u e s to accad e. P er farlo, a g g iu n g e te al m e to d o u n a clau so la throws:
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 609

Sintassi di Java 11.3 La clausola throws

Sintassi m odalitàDiAccesso tip o R e s titu ito nom eMetodo(tipoParametro nomeParametro, ...)


throws C lasseE ccezionel, C lasseEeeezione2, ...

Esempio

public void read (String filename)


Dovete elencare tutte le throws FileNotFoundException, NoSuchElementException
eccezioni a controllo ob­
bligatorio che possono
j Potete elencare anche eccezioni
essere lanciate da questo
l a controllo non obbligatorio.
metodo.

public void readData(String finename) throws FileNotFoundException


{
File inFile = new File(filename);
Scanner in = new Scanner(inFile);

} * * *

La clau so la throws segnala a c h i in v o c a il m e to d o ch e , a sua v o lta , p o trà trovarsi di fro n te


a u n ’e c c e z io n e d i tip o FileNotFoundException. A q u e s to p u n to , tale m e to d o in v o c a n te avrà
di fro n te le m e d e s im e a lte rn a tiv e : g estire l’e c c e z io n e o d ic h ia ra re c h e p u ò essere lan cia ta
dal m e to d o .
N o n g estire u n ’e c c e z io n e q u a n d o si sa c h e p u ò a c c a d e re se m b ra u n c o m p o r ta m e n to
in q u a lc h e m o d o irre sp o n sa b ile , m a , in realtà, è v ero il c o n tra rio :Java m e tte a d isp o siz io n e
d ei p r o g ra m m a to ri u n m e c c a n ism o di g e s tio n e d e lle e c c e z io n i p e r fare in m o d o c h e
cia sc u n a di esse v en g a in v ia ta a u n g e sto re adeguato. A lc u n i m e to d i rile v a n o u n e rro re ,
a ltri m e to d i lo g e s tisc o n o e a ltri a n c o ra lo lascian o s e m p lic e m e n te passare. La clau so la
throws h a s e m p lic e m e n te lo sc o p o di g a ra n tire c h e n essu n a e c c e z io n e si p e rd a p e r strada.

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:

PrintWriter out = new PrintWriter (filename);


writeData(out);
out.closeO; // può darsi che questo enunciato non venga eseguito

S u p p o n ia m o , o ra , c h e u n o d e i m e to d i e s e g u iti p r im a d e ll’u ltim o e n u n c ia to la n c i


L'enunciato t r y con indicazione u n ’e c c e z io n e : in q u e s to caso l’in v o c a z io n e di d o s e n o n v e rrà eseguita! È u n p ro b le m a ,
, di risorse garantisce che la risorsa p e rc h é i d ati sc ritti n el flusso out p o tr e b b e ro n o n a rriv a re n e l file.
^ specificata venga chiusa sia quando La so lu z io n e c o n siste n e ll’u tiliz z o d e ll’e n u n c ia to try con indicazione di risorse.
i il blocco termina normalmente sia D ic h ia ria m o la v aria b ile di tip o PrintWriter n e ll’in te sta z io n e di u n e n u n c ia to try, in q u e s to
quandoviene lanciata un'eccezione. m odo:
610 C a p it o l o 11

Sintassi di Java 11.4 L'enunciato t r y con indicazione di risorse


Sintassi try (T ip o l v a r ia b ile l = es p re s s io n e !; T ip o ! v a iia b ile 2 = e s p res sio n e!; . . . )
{

} ’ ’ ’

Esempio
try (PrintWriter out = new PrintWriter(filename))
{ \
Questo codice può writeData(out);
Implementa !Interfaccia AutoCloseable.
lanciare eccezioni. }

A questo punto viene invocato out .close(),


anche se è stata lanciata un'eccezione.

try (PrintWriter out = new PrintWriter(filename))


{
writeData(out);
} // il metodo out.close() viene sempre invocato

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 :

try (Scanner in = new Scanner(inFile); PrintWriter out = new PrintWriter(outFile))


{
while (in.hasNextLineO)
{
String input = in.nextLine();
String result = process(input);
out.println(result);
}
} // qui vengono invocati in.close() e out.close()

O g n i v o lt a c h e u s a te u n o g g e t t o Scanner o PrintWriter, in s e r it e l i in u n b l o c c o try c o n


i n d ic a z i o n e d i r is o r s e , p e r e sse r e c e r t i c h e tali r is o r s e v e n g a n o c h i u s e in m o d o a p p r o p r ia to .
P iù in g e n e r a le , in ta le str u ttu r a sin ta ttic a p o s s o n o e sse r e d ic h ia r a te v a r ia b ili d i q u a lsia si
c la s se c h e i m p l e m e n t i l ’i n t e r f a c c ia AutoCloseable.

11.4.5 Progettare eccezioni


A v o l t e n e s s u n o d e i tip i d i e c c e z i o n i d i s p o n i b i l i d e s c r iv e a b b a sta n z a b e n e la p a r t ic o la r e
c o n d i z i o n e d i e r r o r e c h e v i in te r e ss a : in tal c a s o , p o t e t e p r o g e t ta r e u n a v o str a c la s se d i
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 611

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.

if (amount > balance)


{
throw new InsufficientFundsException(
"withdrawal of " + amount + " exceeds balance of " + balance);
}

i f descrivere una condizione d'errore, C^ra d o v e t e d e f i n i r e la c la s s e InsufficientFundsException. D e v e e s s e r e u n ’e c c e z i o n e a

progettate una sottoclasse c o n t r o l l o o b b l ig a t o r io o p p u r e n o ? È c a u sa ta da q u a l c h e e v e n t o e s t e r n o o d a u n e r r o r e d e l


di una classe di eccezione esistente. p r o g r a m m a t o r e ? D e c i d i a m o c h e il p r o g r a m m a t o r e a v r e b b e d o v u t o e v ita r e la c o n d i z i o n e
d ’e r r o r e : in fin d e i c o n t i , n o n d o v r e b b e e s s e r e d i f f i c i l e v e r if ic a r e c h e la c o n d i z i o n e amount
<= account.g e t B a l a n c e O sia v e ra p r im a d i i n v o c a r e il m e t o d o withdraw. Q u i n d i , l ’e c c e z i o n e
d o v r e b b e e s s e r e a c o n t r o l l o n o n o b b l i g a t o r i o e d e s t e n d e r e la c la s se RuntimeException o
u n a d e l le s u e s o t t o c la s s i.
B is o g n a d e c id e r e c o n c u r a q u a le c la sse e s t e n d e r e , tra q u e l l e g ià e s is te n ti n e lla g e ra r ch ia :
q u i, ad e s e m p i o , p o t r e m m o c o n s id e r a r e InsufficientFundsException u n c a s o s p e c ia le d i
IllegalArgumentException, c o n s e n t e n d o a d altri p r o g r a m m a t o r i d i c a ttu r a r e q u e s t ’u lt im a
e c c e z i o n e , se n o n s o n o in te r e s s a ti a ll’e sa tta n a tu ra d e l p r o b le m a .
S o l i t a m e n t e in u n a c la s se c h e d e f i n i s c e e c c e z i o n i si f o r n i s c o n o d u e c o s t r u t t o r i: u n o
s e n z a a r g o m e n t i e u n o c h e a c c e tta u n a str in g a c o m e m e s s a g g io c h e d e s c r iv e la m o t i v a z i o n e
d e l l ’e c c e z i o n e . E c c o la d ic h i a r a z i o n e d e lla cla sse:

public class InsufficientFundsException extends IllegalArgumentException


{
public InsufficientFundsExceptionO {}

public InsufficientFundsException(String message)


{
super(message);
}
}

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.");
}
}

s u p p o n e t e c h e il file input.txt esista e sia p r iv o di c o n t e n u t o : s e g u it e p a sso d o p o p a sso il


flu sso di e s e c u z io n e .
1 9 . P e r c h é ArrayIndexOutOfBoundsException è u n ’e c c e z i o n e a c o n t r o llo n o n o b b lig a to r io ?
2 0 . C ’è d ifferen za tra cattu rare u n ’e c c e z i o n e a c o n tr o llo o b b lig a to r io e cattu rare u n ’e c c e z io n e
a c o n t r o llo n o n o b b lig a to r io ?
2 1 . C o s a c ’è d i sb a g lia to n e l c o d i c e s e g u e n t e e c o m e lo si p u ò c o r r e g g e r e ?
public static void writeAll(String[] lines. String filename)
{
PrintWriter out = new PrintWriter(filename);
for (String line : lines)
{
out.printIn(line.toUpperCase());
}
out.closeO;
}
2 2 . A c o s a s e r v e l ’i n v o c a z i o n e su pe r( me ss ag e) n e l s e c o n d o c o s t r u t t o r e d i Insufficient-
FundsException?
2 3 . I m m a g in a te c h e il v o s tr o p r o g r a m m a a c q u isis c a d ati di c o n t i b a n c a r i le g g e n d o li da u n
file. D iv e r s a m e n te da q u a n to p r e v isto , u n v a lo re n o n è di tip o double e d e c id e t e di lan ciare
u n ’e c c e z i o n e di tip o BadDataException. Q u a le classe d i e c c e z i o n i d o v r e ste e ste n d e r 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 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

Lanciare presto, catturare tardi


Q uando un m e to d o in d iv id u a un p r o b le m a che non è in grado
________

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.

Lanciate un'eccezione non appena P e r c o n t r o , u n m e t o d o d o v r e b b e c a ttu r a r e u n ’e c c e z i o n e s o l t a n t o se è e f f e t t i v a m e n t e


si riscontra un problema, in g r a d o d i r is o lv e r e il p r o b le m a c h e è s o r t o , a l t r im e n t i il r i m e d i o m i g li o r e c o n s i s t e n e l
ma catturatela soltanto quando la sc ia r e c h e l ’e c c e z i o n e si p r o p a g h i al m e t o d o i n v o c a n t e , c o n s e n t e n d o n e la c a ttu r a da
il problema può essere gestito. p a r te d i u n g e s t o r e c o m p e t e n t e .
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 613

Q u e s ti p r in c ip i p o s s o n o essere ria ss u n ti in u n m o tto : “ la n c ia re p re sto , c a ttu ra re


ta r d i” .

I Suggerimenti perla programmazione 11,2......


Non mettete a tacere le eccezioni
................... . .. ........... ......

Q u a n d o in v o c ate u n m e to d o c h e lancia u n ’e c c e z io n e a c o n tro llo o b b lig a to rio e n o n n e


avete d e fin ito il gestore, il c o m p ila to re p ro testa. N e ll’ansia di p o rta re a te rm in e il vostro
lavoro, è co m p re n sib ile c h e zittiate il c o m p ila to re mettendo a tacere (“ sq u e lc h in g ” ) l’ec cezio n e:

try
{
Scanner in = new Scanner(new File(filename));
// il compilatore protestava per FileNotFoundException

}
catch (FileNotFoundException e) {} // ecco fatto!

11 g e sto re di e c c e z io n e v u o to fa c re d e re al c o m p ila to re c h e l’e c c e z io n e sia stata g estita,


m a , a lu n g o te rm in e , q u e sta è c h ia ra m e n te u n a ca ttiv a id ea. Le e c c e z io n i s o n o state
p ro g e tta te p e r trasferire u n p ro b le m a a u n g e sto re c o m p e te n te : l’in s e rim e n to di u n g esto re
in c o m p e te n te n a sc o n d e s e m p lic e m e n te u n a c o n d iz io n e d ’e rro re c h e p o tre b b e essere seria.

.JiuflflfiiimfintI ttfiiJajiQflriainmazioae 11,3.____ ___ __ ___ ______ _


I Lanciate eccezioni veramente specifiche
Q u a n d o la n c ia te u n ’e c c e z io n e , d o v re ste se m p re sceg liere u n a classe di e c c e z io n i c h e
d escriv a n el m o d o p iù p re c iso p o ssib ile la s itu a z io n e d ’e rro re c h e si è v erific ata . A d
e se m p io , q u a n d o u n c o n to b a n c a rio n o n h a fo n d i su ffic ie n ti p e r c o n s e n tire u n p reliev o ,
sa re b b e u n a p essim a id e a la n c ia re s e m p lic e m e n te u n o g g e tto RuntimeException, p e rc h é
c iò r e n d e re b b e m o lto p iù co m p lessa la c a ttu ra d e ll’e c c e z io n e . Se, in fa tti, c a ttu ra s te tu tte
le e c c e z io n i di tip o RuntimeException, la v o stra clau so la catch v e rre b b e attiv ata a n c h e da
e c c e z io n i c o m e NullPointerException, ArraylndexOutOfBoundsException e così via. D o v re ste
in tal caso isp e z io n a re a tte n ta m e n te l’o g g e tto c h e ra p p re se n ta l’e c c e z io n e , te n ta n d o di
c a p ire se q u e sta sia stata p ro v o c a ta da fo n d i in su ffic ie n ti o p p u re no.
Se la lib re ria sta n d a rd n o n c o n tie n e u n ’e c c e z io n e c h e d e sc riv e la v o stra p a rtic o la re
situ a z io n e di e rro re , d e fin ite se m p lic e m e n te u n a n u o v a classe di e c c e z io n i.

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 :

public void deposit(double amount)


{
614 C a p it o l o 11

assert amount >= 0;


balance = balance + amount;
}

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:

java -enableassertions ClassePrincipale

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.

Argomenti avanzati 11.7 _______ __________


L'enunciato tr y / f in a lly
N e l P a ra g r a fo 1 1 . 4 a v e t e v i s t o c o m e g a r a n tir e c h e u n a r is o r s a v e n g a c h iu s a a n c h e q u a n d o
si v e r if ic a u n ’e c c e z i o n e . L’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 i n v o c a il m e t o d o d o s e
c o n t u t t e le v a r ia b ili d ic h ia r a t e n e lla p r o p r ia in t e s t a z io n e : p e r c h i u d e r e r is o r s e d o v r e s t e
s e m p r e u sa re q u e lla t e c n ic a .
A v o l t e , p e r ò , p u ò s u c c e d e r e c h e si a b b ia b i s o g n o d i e s e g u ir e a lc u n e a z io n i d i “ p u l iz ia ”
c h e n o n s ia n o l ’i n v o c a z i o n e d e l m e t o d o d o s e . In tal c a s o , u s a te l ’e n u n c i a t o t r y /f in a lly :

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

Applicazione: gestione di errori in ingtt^p


Q u e s to p a ra g ra fo an alizza u n e s e m p io c o m p le to di p ro g ra m m a c h e u tilizz a la g e s tio n e
di e c c e z io n i. Il p ro g ra m m a c h ie d e all’u te n te il n o m e di u n file, c h e d ev e c o n te n e re dati
se c o n d o q u e ste sp ecifich e: la p rim a rig a d el file c o n tie n e il n u m e ro to ta le di v alo ri p re se n ti
n el se g u ito , m e n tr e le rig h e successive c o n te n g o n o i d ati v e ri e p r o p ri, u n o p e r rig a. U n
tip ic o file di d ati in in g resso p o tr e b b e essere sim ile a q u e s to :

3
1.45
- 2.1
0.05

Durante la progettazione C o s a p u ò a n d a re s to rto ? C i s o n o d u e risch i p rin c ip a li.


di un programma, chiedetevi
che tipo di eccezioni si possono • Il file p o tr e b b e n o n esistere.
verificare. • Il file p o tr e b b e c o n te n e re d ati in u n f o rm a to e rra to .

C h i p u ò in d iv id u a re tali e rro ri? Se il file n o n esiste, il c o s tru tto r e di Scanner la n ce rà


u n ’e c c e z io n e . I m e to d i c h e e la b o ra n o i v a lo ri in in g resso d e v o n o la n cia re u n ’e c c e z io n e
q u a n d o id e n tific a n o u n e rro re n el f o rm a to d ei d ati.
Q u a li e c c e z io n i p o sso n o essere lan cia te ? Q u a n d o il file n o n esiste, il c o s tru tto re di
Scanner lancia u n ’e c c e z io n e FileNotFoundException, c h e è p e rfe tta p e r tale situ az io n e. Q u a n d o
il file di d ati h a u n fo rm a to n o n c o rre tto , Ia n c e re m o u n ’e c c e z io n e a c o n tro llo o b b lig a to rio
p ro g e tta ta da n o i, BadDataException. U sia m o u n ’e c c e z io n e a c o n tro llo o b b lig a to rio p e rc h é
la c o r r u z io n e d ei d ati in u n file sfu g g e al c o n tro llo d el p ro g ra m m a to re .
^ciascuna eccezione dovete decidere] C h i p u ò p o r re rim e d io agli e r r o ri seg n alati da tali e c c e z io n i? Il m e to d o main del
quale zona del programma la può | p ro g ra m m a DataAnalyzer è l’u n ic o a in te ra g ire c o n l’u te n te , p e r cu i c a ttu ra qualsiasi
gestire in modo competente. e c c e z io n e , visu alizza u n m e ssag g io d ’e rro re a p p ro p ria to e c o n c e d e all’u te n te u n ’altra
p o ssib ilità p e r fo rn ire u n file c o rre tto .

File DdtaAnalyzer.java
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;

Questo programma legge un file contenente numeri e ne analizza


il contenuto. Se il file non esiste o contiene stringhe che non siano
numeri, visualizza un messaggio d'errore.
*/
public class DataAnalyzer
{
public static void main(String[] args)
{
Scanner in = nevj Scanner(System.in);
DataSetReader reader = new DataSetReader();

boolean done = false;


while (!done)
616 C a p it o l o 11

try
{
Sy stem. out. print In ("Please enter the file name: ");
String filename = in.next();

double[] data = reader.readFile(filename);


double sum = 0;
for (double d : data) { sum = sum + d; }
System.out.println("The sum is " + sum);
done = true;
}
catch (FileNotFoundException exception)
{
System.out.println("File not found.");
}
catch (BadDataException exception)
{
System.out.println("Bad data: " + exception.getMessage());
}
catch (IOException exception)
{
exception.printStackTrace();
}

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 .

public double[] readFile(String filename) throws IOException


{
File inFile = new File(filename);
try (Scanner in = new Scanner(inFile))
{
readData(in);
return data;
}
}

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 .

private void readData(Scanner in) throws BadDataException


{
if (!in.hasNextIntO)
{
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 617

throw new BadDataException("Length expected'');


}
int numberOfValues = in.nextlnt();
data = new double[numberOfValues];

for (int i = 0; i < numberOfValues; i++)


{
readValue(in, i);
}

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:

private void readValue(Scanner in, int i) throws BadDataException


{
if (!in.hasNextDoubleO)
{
throw new BadDataException("Data value expected");
}
data[i] = in.nextDoubleO;
}

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

trovano i metodi readData e readFile, che semplicemente trasferiscono le eccezioni a chi


li ha invocati.

File DdtaSetReader.java
import java.io.File;
import java.io.IOException;
import java.util.Scanner;

Legge un insieme di valori da un file, che deve avere questo formato:


numeroDiValori
vaiorei
valore2

*/
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

private void readData(Scanner in) throws BadDataException


{
if (!in.hasNextIntO)
{
throw new BadDataException("Length expected");
}
int numberOfValues = in.nextInt();
data = new double[numberOfValues];

for (int i = 0; i < numberOfValues; i++)


{
readValue(in, i);
}
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 619

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.

Leggere e scrivere testo memorizzato in file


• Per leggere file di testo usate la classe Scanner.
• Per scrivere file di testo usate la classe PrintW riter e i su o i m e to d i p r in t /p r in t ln /p r in t f .
• Q u a n d o avete fin ito di elaborare un file, c h iu d e te lo .

Elaborare dati in file di testo


• Il m e t o d o n ext le g g e una strin ga d elim itata da caratteri di spaziatura.
• La classe Character c o n tie n e m e to d i u tili per classificare i caratteri.
• Il m e to d o nextLine le g g e u n ’intera riga.
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 621

• Se una stringa contiene le cifre di un numero, se ne può ottenere il valore invocando Integer,
parseint oppure Double.parseDouble.

Elaborare gli argomenti presenti sulla riga dei comandi


• 1 programmi che vengono eseguiti dalla riga di comando ricevono i relativi argomenti nel
metodo main.
Trasferire il controllo dalla generazione di un errore alla sua gestione
• Per segnalare una c o n d iz io n e e c c e z io n a le si usa l’en u n c ia to throw, lanciando un oggetto
di tipo “e cc e z io n e ” .
• Quando si lancia un’eccezione, l’elaborazione continua in un gestore dell’eccezione.
• Inserite all’interno di un blocco try gli enunciati che possono lanciare un’eccezione, scrivendo
poi il gestore in una clausola catch.
• Le eccezioni a controllo obbligatorio sono dovute a circostanze esterne che il programmatore
non può evitare e il compilatore verifica che il programma le gestisca.
• Aggiungete la clausola throws a un metodo che può lanciare un’eccezione a controllo ob­
bligatorio.
• L’enunciato try con indicazione di risorse garantisce che la risorsa specificata venga chiusa sia
quando il blocco termina normalmente sia quando viene lanciata un’eccezione.
• Per descrivere una condizione d’errore, progettate una sottoclasse di una classe di eccezione
esistente.
• Lanciate un’eccezione non appena si riscontra un problema, ma catturatela soltanto quando
il problema può essere gestito.
Usare la gestione delle eccezioni in un programma che acquisisce dati
• Durante la progettazione di un programma, chiedetevi che tipo di eccezioni si possono
verificare.
• Per ciascuna eccezione dovete decidere quale zona del programma la può gestire in modo
competente.

ja v a .io .F ile java. la n g.RuntimeExeeption


java.io.FileNotFoundException ja v a .la n g .String
java.io.IOException replaeeAll
j ava.io.PrintWriter sp lit
elose java. lang. Throwable
ja v a .la n g .AutoCloseable getMessage
ja v a.la n g.Charaeter printStaekTraee
isD igit ja v a .n e t.URL
isLetter openStream
i s LowerCase java. U t il. InputMismatehExeeption
isUpperCase java. U t il. NoSuehElementExeeption
isWhiteSpaee ja v a .u t il.Seanner
ja v a.la n g.Double elose
parseDouble hasNextLine
ja v a.la n g .Error nextLine
java. lan g.IllegalArgumentExeeption useDelimiter
ja v a.la n g .Integer j avax. swing. DFileChooser
parseint getSeleetedFile
ja v a.la n g.NullPointerExeeption showOpenDialog
java. lang. NumberFormatExeeption showSaveDialog
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

quali sono i valori di args[o], args[l] e così via?


R l 1.6. Qual è la differenza tra lanciare e catturare un’eccezione?
R l 1.7. Cos’è un’eccezione a controllo obbligatorio? Cos’è un’eccezione a controllo non obbli­
gatorio? Fate un esempio di entrambe le categorie. Quali eccezioni siete obbligati a dichiarare
con una clausola throws?
R l 1.8. Perché non è obbligatorio dichiarare che un metodo può lanciare un’eccezione IndexOu-
tOfBoundsException?

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!

il programma deve scrivere il file:


/* 1 */ Mary had a little lamb
/* 2 */ Whose fleece was white as snow.
/* 3 */ And everywhere that Mary went,
/* 4 */ The lamb was sure to go!
624 C a p it o l o 11

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

report.txt: has broken up an international ring of DVD bootleggers that


address.txt: Kris Kringle, North Pole
address.txt: Homer Simpson, Springfield
Homework, java: String filename;

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

il contenuto di HelloPrinter.java diventa


retnirPolleH ssalc cilbup
{
)sgra ][gnirtS(niam diov citats cilbup
{
;)"!dlroW ,olleH"(nltnirp.tuo.metsyS
}
}
Naturalmente eseguendo due volte Reverse sullo stesso file si ottiene di nuovo il file originale.
El 1.11. Scrivete un programma che legga un file, una riga per volta, e ne scriva le righe in un
altro file in ordine inverso. Se, ad esempio, il file input.txt ha questo contenuto:
Mary had a little lamb
Whose fleece was white as snow
And everywhere that Mary went
The lamb was sure to go.
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 625

e il programma viene eseguito in questo modo:


java ReverseFile input.txt output.txt

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

Obiettivi del capitolo


Imparare a “pensare ricorsivamente”
Essere in grado di usare metodi ausiliari ricorsivi
Capire la relazione esistente tra ricorsione e iterazione
Capire come l’uso della ricorsione si ripercuote sull’efficienza di un algoritmo
Analizzare problemi che sono molto più semplici da risolvere con la ricorsione che
con l’iterazione
• Elaborare dati aventi una struttura ricorsiva usando la ricorsione mutua

La ricorsione è una potente tecnica per scomporre complessi problemi computazionali


in problemi più semplici e spesso anche di minore dimensione. 11 termine “ricorsione”
O “ricorrenza” si riferisce al fatto che la medesima elaborazione “ricorre”, cioè accade
ripetutamente, mentre il problema viene risolto. La ricorsione è spesso il modo più na­
turale di pensare a un problema e alcune elaborazioni sono molto difficili da portare a
termine senza la ricorsione. Questo capitolo vi mostra esempi semplici e complessi di
ricorsione e vi insegna a “pensare ricorsivamente”.
628 C a p it o l o 12

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:

[]
[][]
[][][]

Vorremmo calcolare l’area di un triangolo avente ampiezza n, nell’ipotesi che ciascun


quadrato [] abbia area unitaria: questo valore viene a volte chiamato numero triangolare
n-esimo e, osservando l’esempio, siamo in grado di affermare che il terzo numero triangolare
è 6.
Può darsi che sappiate che esiste una formula molto semplice per calcolare questi
numeri, ma per il momento dovete supporre di non conoscerla. L’obiettivo finale di questo
paragrafo non è quello di calcolare numeri triangolari, ma di comprendere il concetto
di ricorsione in una situazione semplice.
Ecco una traccia della classe che svilupperemo:
public class Triangle
{
private int width;

public Triangle(int aWidth)


{
width = aWidth;
}
public int getA re aO
{
}
}

Se l’ampiezza del triangolo è 1, allora il triangolo consiste di un unico quadrato e la sua


area vale 1. Occupiamoci prima di questo caso.
public int g e tA re aO
{
if (width == I) { return l; }

} ’ ' '

Per trattare il caso generale, consideriamo questa figura.

[]
[][]
[][][]
[][][][]

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!

Triangle smallerTriangle = new Triangle(width - l);


int smallerArea = smallerTriangle.getArea();

A questo punto siamo in grado di completare il metodo getArea:

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 .

Lo schema delle invocazioni di un metodo ricorsivo sembra complicato: in effetti, la


chiave per il successo nella progettazione di un metodo ricorsivo è n o n p e n s a r e a l l a r i c o r s i o n e .
Invece, osservate ancora una volta il metodo getArea e notate come sia tremendamente
ragionevole. Se l’ampiezza è 1, ovviamente l’area è 1, e la parte successiva del metodo è
altrettanto ragionevole: calcola l’area del triangolo più p i c c o l o , s e n z a p e n s a r e a c o m e q u e s t o
p o s s a e s s e r e f a t t o , e aggiunge l’ampiezza, ottenendo chiaramente l’area del triangolo più

grande.
Esistono due requisiti che sono basilari per il corretto funzionamento di una ricorsione:•

• Ogni invocazione ricorsiva deve semplificare in qualche modo l’elaborazione.


• Devono esistere casi speciali che gestiscano in modo diretto le elaborazioni più
semplici.
630 C a p it o l o 12

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

e, ovviamente, possiamo calcolarla con un semplice ciclo:


double area = 0;
for (int i = 1; i <= width; i++)
{
area = area + i;
}

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:

1 + 2 + ...+ « —«X (m + l)/2

Quindi, l’area vale


width * (width + 1 ) / 2

In sostanza, per risolvere questo problema non erano necessari né la ricorsione, né un


ciclo. La soluzione ricorsiva è da intendersi come una fase di “riscaldamento” per il
paragrafo successivo.

FileTriangle.javd

Una forma triangolare composta di quadrati unitari impilati, come questa:


[]
[][]
[][][]
*/
public class Triangle
{
private int width;

/**
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;
}

Calcola l'area del triangolo.


^return l'area
*/
public int g e tA re aO
{
if (width <= O) { return 0; }
else if (width == l) { return I; }
else
{
Triangle smallerTriangle = new Triangle(width - l);
int smallerArea = smallerTriangle.getArea();
return smallerArea + width;
}
}
}

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");
}
}

Esecuzione del programma


Area: 55
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;
}
}

Quanto vale mystery(4)?

Perfarpratica
A questo punto si consiglia di svolgere gli esercizi E 12.1, E 12.2 e E l2.12, al termine del
capitolo.

fuori tomuni 12.1___ ___________ ____________ ______ _


Ricorsione infinita
Un errore di programmazione molto comune è la ricorsione infinita: un metodo invoca
se stesso ripetutamente, senza che si intraveda la fine di questo processo. 11 computer ha
bisogno di una certa quantità di memoria per gestire ciascuna invocazione, per cui, dopo
un certo numero di invocazioni, si esaurisce la memoria disponibile per tale scopo e il
programma termina bruscamente segnalando “stack overflow” (un errore di trabocco,
oveijìow, nella pila, stack, che gestisce le invocazioni).
La ricorsione infinita accade perché i valori dei parametri non diventano più semplici
oppure perché manca un caso speciale che ponga fine alle invocazioni. Ad esempio,
supponiamo che il metodo getArea debba calcolare l’area del triangolo di ampiezza 0: se
non fosse per la verifica del caso speciale, il metodo costruirebbe triangoli con ampiezze
- 1 ,- 2 ,- 3 , e così via.

Errori comuni 12.2


Effettuare il tracciamento dell'esecuzione di metodi ricorsivi
Identificare gli errori in un metodo ricorsivo può essere una vera sfida. Quando, durante
una sessione di debugging, impostate un punto di arresto (breakpoint) in un metodo
R ic o r s io n e 633

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

• A man, a pian, a canal —Panama!


• Go hang a salami. I’m a lasagna hog

e, ovviamente, il palindromo più antico

• Madam, I’m Adam

Quando si deve verificare se una stringa è un palindromo si trascurano le differenze tra


maiuscole e minuscole e si ignorano gli spazi e i segni di punteggiatura.
Vogliamo realizzare il metodo isPalindrome nella classe seguente:

public class Palindromes


{

Verifica se una stringa è un palindromo.


@param text la stringa da verificare
@return true se e solo se questa frase è un palindromo
*/
public static boolean isPalindrome(String text)
{
}
}

Fase I Considerate diversi modi per semplificare i dati


Focalizzate il vostro pensiero su un particolare dato (o insieme di dati) in ingresso per il
problema che volete risolvere. Pensate a come poter semplificare i dati in modo che si
possa porre lo stesso problema con dati più semplici.
Nel considerare dati più semplici, tipicamente si elimina soltanto una piccola parte
dei dati originali: ad esempio, un carattere o due caratteri di una stringa, oppure una
piccola porzione di una forma geometrica. Altre volte, invece, è più utile dividere i dati
a metà e cercare di capire cosa significhi risolvere il problema separatamente per le due
parti.
Nel problema della verifica di un palindromo, il dato in ingresso è la stringa che
vogliamo verificare. Come possiamo semplificare tale dato? Ecco alcune possibilità

Eliminare il primo carattere.


Eliminare l’ultimo carattere.
Eliminare il primo e l’ultimo carattere.
Eliminare il carattere centrale.
Dividere la stringa a metà.

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"

a metà, ottenete due stringhe:


"Madam, I"

'"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

• la prima e l’ultima lettera sono uguali (trascurando le differenze tra maiuscole e


minuscole)

• la parola che si ottiene eliminando la prima e l’ultima lettera è un palindromo.

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

"A man, a pian, a canal. Panama I "

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.

Fase 3 Trovate le soluzioni per i casi più semplici


Un’elaborazione ricorsiva continua a semplificare i propri dati finché arriva a casi molto
semplici. Per essere certi che la ricorsione termini, dovete gestire separatamente tali casi
più semplici, identificando soluzioni speciali, cosa che generalmente è molto facile.
A volte, però, capita di addentrarsi in questioni filosofiche che riguardano la gestione
di casi degeneri: stringhe vuote, forme geometriche di area nulla, e così via. In tali casi
dovete prendere in considerazione un dato in ingresso un po’ più complicato che venga
ridotto a tale situazione banale e vedere quale valore dovreste attribuire al caso degenere
per fare in modo che al caso un po’più complesso, calcolato secondo le regole identificate
nella Fase 2, venga assegnato il valore corretto.
Diamo un’occhiata alle stringhe più semplici per la verifica di un palindromo:

• stringhe con due caratteri


• stringhe con un solo carattere
• stringa vuota

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

rimanente (cioè la stringa vuota) è un palindromo. Quindi, ha senso affermare che la


stringa vuota è un palindromo.
Una stringa costituita da un’unica lettera, come "I", è un palindromo. Cosa possiamo
dire in merito a una stringa contenente un unico carattere, che però non sia una lettera,
come Eliminando il carattere ! si ottiene la stringa vuota, che è un palindromo. In
definitiva, tutte le stringhe di lunghezza O e l sono palindromi.

Fase 4 Implementate la soluzione combinando i casi semplici e il passo di semplificazione


Ora siete pronti per scrivere la soluzione. Gestite separatamente i casi dei dati speciali
individuati nella Fase 3. Se i dati non rientrano in uno di questi casi più semplici, seguite
il ragionamento delineato nella Fase 2.
Ecco il metodo isPalindrome.
public static boolean isPalindrofne(String text)
{
int length = text.length();

// considera separatamente i casi delle stringhe più brevi


if (length <= I) { return true; }
else
{
// prendi il primo e l'ultimo carattere, convertiti in minuscolo
char first = Character.toLowerCase(text.charAt(0));
char last = Character.toLowerCase(text.charAt(length - l));

if (Character.isLetter(first) && Character.isLetter(last))


{
// entrambi sono lettere
if (first == last)
{
// elimina il primo e l'ultimo carattere
String shorter = text.substring(l, length - l);
return isPalindrome(shorter);
}
else
{
return false;
}
}
else if (!Character.isLetter(last))
{
// elimina l'ultimo carattere
String shorter = text.substring(o, length - l);
return isPalindrome(shorter);
}
else
{
// elimina il primo carattere
String shorter = text.substring(l);
return isPalindrome(shorter);
}
638 C a p it o l o 12

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

vi consente di dissipare il dubbio. Il metodo

File[] IistFilesO

restituisce un array contenente tutti gli oggetti File contenuti in una cartella: possono
essere semplici file o altre cartelle.

Fase 1 Considerate diversi modi per semplificare i dati


Il problema opera su due dati: un oggetto File, che rappresenta un albero di cartelle, e
un’estensione per nomi di file. È evidente che manipolando l’estensione non riusciamo
a semplificare il problema, mentre c’è un modo ovvio per sfoltire l’albero di 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.

Per ogni oggetto File nella radice


Se l'oggetto File è una cartella
Cerca ricorsivamente in tale cartella.
Altrimenti se il nome termina con l'estensione richiesta
Visualizza il nome.

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.

Fase 3 Trovate le soluzioni per i casi più semplici


Il caso più semplice è costituito da un file che non sia una cartella: controlliamo
semplicemente se il suo nome termina con l’estensione richiesta e, in caso affermativo,
lo visualizziamo.
R ic o r s io n e 639

Fase 4 Implementate la soluzione combinando i casi semplici e il passo di semplificazione


Progettiamo la classe FileFinder, dotata di un metodo per la ricerca dei file desiderati.

public class FileFinder


{
private File[] children;

Costruisce un oggetto che cerca file in un albero di cartelle.


@param startingDirectory la radice dell'albero di cartelle
*/
public FileFinder(File startingDirectory)
{
children = startingDirectory.listFiles();
}
/*
Visualizza tutti i file il cui nome termina con l'estensione prevista.
@param extension un'estensione per file (come ".java")
*/
public void find(String extension)
{

}
}

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.
}

Ecco il metodo find completo:

Visualizza tutti i file il cui nome termina con l'estensione prevista.


@param extension un'estensione per file (come ".java")
*/
public void find(String extension)
{
for (File child : children)
{
String fileName = child.toStringO;
if (child.isDirectoryO)
{
FileFinder finder = new FileFinder(child);
finder.find(extension);
}
else if (fileName.endsWith(extension))
640 C a p it o l o 12

System.ou t.print In (fileName) ;

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:

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, estension);
}
}
else
{
String fileName = aFile.toStringO;
if (fileName.endsWith(extension))
{
System.ou t.print In (fileName ) ;
}
}
}

La strategia è sostanzialmente identica: esaminando un file, verifichiamo se il suo nome


termina con l’estensione richiesta, nel qual caso lo visualizziamo; esaminando, invece,
una cartella, eseguiamo l’esame per tutti i file e le sotto-cartelle presenti al suo interno.
In questa soluzione abbiamo deciso di accettare, come punto di partenza, sia un file
sia una cartella e, per questo motivo, la modalità di invocazione è leggermente diversa.
Nella prima soluzione le invocazioni ricorsive vengono efifettuate soltanto con cartelle,
mentre nella seconda soluzione si invoca il metodo ricorsivo su tutti gli elementi presenti
nell’array restituito da listFiles(), anche se, nel caso di invocazione con un file semplice,
la ricorsione termina subito.

File FileFinder2.javd
import java.io.File;

public class FileFinderZ


{
public static void main(String[] args)
{
File StartingDirectory = new File('7home/myname'');
find(startingDirectory, ".java");

/**
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) ;
}
}

12.2 Metodi ausiliarilicorsivi


A volte è più semplice trovare una soluzione ricorsiva dopo aver apportato una piccola
A volte è più semplice trovare
una soluzione ricorsiva dopo aver
modifica al problema originario, che può, poi, essere risolto invocando un metodo
apportato una piccola modifica
ausiliario ricorsivo.
al problema originario. Ecco un esempio tipico. Considerate la verifica di palindromo vista nei Consigli
pratici 12.1: costruire nuove stringhe a ogni passo è poco efficiente. Provate a considerare
la seguente modifica al problema: invece di verificare se l’intera frase è un palindromo,
verifichiamo se una sottostringa è un palindromo.

/**
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

public static boolean isPalindrome(String text, int start, int end)


{
11 considera separatamente i casi delle stringhe di lunghezza O e l
if (start >= end) { return true; }
else
{
// prendi il primo e l'ultimo carattere, convertiti in minuscolo
char first = Character.toLowerCase(text.charAt(start));
char last = Character.toLowerCase(text.charAt(end));

if (Character.isLetter(first) && Character.isLetter(last))


{
if (first == last)
{
// verifica la sottostringa che non contiene le due lettere uguali
return isPalindrome(start + l, end - l);
}
else
{
return false;
}
}
else if (!Character.isLetter(last))
{
// verifica la sottostringa che non contiene l'ultimo carattere
return isPalindrome(start, end - l);
}
else
{
// verifica la sottostringa che non contiene il primo carattere
return isPalindrome(start + 1, end);
}
}
}

Dovreste comunque fornire un metodo per risolvere il problema complessivo, perché


l’utente del vostro metodo non è tenuto a conoscere i trucchi che riguardano le posizioni
nella sottostringa. Invocate semplicemente il metodo ausiliario con valori di posizioni
che provochino la verifica dell’intera stringa:
public static boolean isPalindrome(String text)
{
return isPalindrome(text, 0, text.length() - l);
}

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.

123 L'efficienza della ricorsione


Come avete visto in questo capitolo, la ricorsione può essere uno strumento potente
per realizzare algoritmi complessi, ma può anche portare ad algoritmi con prestazioni
scadenti. In questo paragrafo ci porremo il problema di capire quando la ricorsione sia
un bene e quando, invece, sia inefficiente.
Considerate la sequenza di Fibonacci, una sequenza di numeri definita da queste
equazioni:

/. = 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

E facile estendere indefinitamente la sequenza: basta aggiungere la somma degli ultimi


due valori presenti. Ad esempio, il valore successivo è 34 + 55 = 89.
Vorremmo scrivere un metodo che calcola/, per qualsiasi valore di n\ traduciamo
direttamente la definizione in un metodo ricorsivo.

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

Scanner in = new Scanner(System.in);


System.out.print("Enter n: ");
int n = in.nextInt0 ;

for (int i = i; i <= n; i++)


{
long f = fib(i);
Sy stem. out. print In ("fib(" + i + ") = " + f);
}
}
/**
Calcola un numero di Fibonacci.
@param n un numero intero
@return l'n-esimo numero di Fibonacci
*/
public static long fib(int n)
{
if (n <= 2) { return l; }
else { return fib(n - l) + fib(n - 2); }
}
}

Esecuzione del programma

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

Questo è sicuramente semplice e il metodo funziona in modo corretto, ma osservate


attentamente la visualizzazione dei dati prodotti mentre eseguite il programma di prova.
Le prime invocazioni del metodo fib sono abbastanza veloci, ma, per valori maggiori, il
programma lascia trascorrere una quantità sorprendente di tempo tra due visualizzazioni.
Questo non ha senso: armati di carta e penna, nonché di una calcolatrice tascabile,
potreste calcolare questi numeri piuttosto in fretta, per cui il computer non dovrebbe
assolutamente impiegare tanto tempo.
Per identificare il problema, inseriamo nel metodo alcuni m essaggi di tracciatura
{trace message).

File RecursiveFibTracer.java
import java.util.Scanner;

Questo programma visualizza messaggi che mostrano quanto spesso.


R ic o r s io n e 645

per calcolare i numeri di Fibonacci, il metodo ricorsivo invoca se stesso.


*/
public class RecursiveFibTracer
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter n: ");
int n = in.nextIntO;

long f = fib(n);

System.out.println("fib(" + n + ") = " + f);


}

Calcola un numero di Fibonacci.


@param n un numero intero
@return l'n-esimo numero di Fibonacci
*/
public static long fib(int n)
{
System.out.println("Entering fib: n = " + n);
long f;
if (n <= 2) { f = I; }
else { f = fib(n - l) + fib(n - 2); }
System.out.println("Exiting fib: n = " + n
+ " return value = " + f);
return f;
}
}

Esecuzione del programma

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)

Schema delle invocazioni


del m etodo ricorsivo fib
fib(5) fib(4)

fib(4) fib(3) fib(3) fib(2)

/ \
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;

for (int i = 1; i <= n; i++)


R ic o r s io n e 647

long f = fib(i);
System, out. print In ("fib (" + i + ") = " + f);
}
}

Calcola un numero di Fibonacci.


@param n un numero intero
^return l'n-esimo numero di Fibonacci
*/
public static long fib(int n)
{
if (n <= 2) { return l; }
else
{
long olderValue = l;
long oldValue = l;
long newValue = l;
for (int i = 3; i <= n; i++)
{
newValue = oldValue + olderValue;
olderValue = oldValue;
oldValue = newValue;
}
return newValue;
}
}
}

Esecuzione del programma


Enter n: 50
fib(l) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13

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

int end = text.length() - I;


while (start < end)
{
char first = Character.toLowerCase(text.charAt(start));
char last = Character.toLowerCase(text.charAt(end));

if (Character.isLetter(first) && Character.isLetter(last))


{
// entrambi i caratteri sono lettere
if (first == last)
{
start++;
end--;
}
else { return false; }
}
if (!Character.isLetter(last)) { end— ; }
if 0 Character. isLetter (first)) { start++; }
}
return true;
}

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 "

A questo punto ci serve un’idea per generare le permutazioni ricorsivamente. Consideriamo


la stringa " e a t " e cerchiamo di semplificare il problema: dapprima genereremo tutte le
permutazioni che iniziano con la lettera 'e',poi quelle che iniziano con 'a',infine quelle
che iniziano con 't'. Come facciamo a generare le permutazioni che iniziano con 'e'?
Ci servono le permutazioni della sottostringa " a t " . Ma questo non è altro che il problema
originario (cioè la generazione di tutte le permutazioni di una stringa) con un dato
di ingresso più semplice: la stringa più breve " a t " . Possiamo quindi usare la ricorsione,
generando le permutazioni della sottostringa " a t " , che sono:
" a t"
"ta "

A ogni permutazione di tale sottostringa si inserisce la lettera 'e' come prefisso,ottenendo


le permutazioni di " e a t " che iniziano con ' e ' :
650 C a p it o l o 12

"eat"
"eta"

C o n s id e r ia m o o ra le p e r m u ta z io n i di "eat" c h e in iz ia n o c o n 'a '. D o b b ia m o g e n e ra re le


p e r m u ta z io n i d elle le tte re rim a n e n ti, "e t", c h e so n o :

"et"
"te"

A g g iu n g ia m o la le tte ra 'a ' all’in iz io d elle s trin g h e e o tte n ia m o :

"aet"
"ate"

A llo stesso m o d o g e n e ria m o le p e rm u ta z io n i c h e in iz ia n o c o n la le tte ra ' t ' .


A v u ta l’id e a, !’im p le m e n ta z io n e è quasi b an a le . N e l m e to d o permutations, sc riv ia m o
u n ciclo c h e p re n d a in esam e tu tte le p o siz io n i all’in te r n o della p aro la c h e d ev e essere
p e r m u ta ta ; p e r c ia sc u n a p o s iz io n e , /, c a lc o lia m o la p a ro la p iù b re v e c h e si o ttie n e
e lim in a n d o il c a ra tte re /-e sim o :

String shorter = word.substring(0, i) + word.substring(i + l);

C a lc o lia m o le p e rm u ta z io n i d i tale p aro la p iù b rev e:

ArrayList<String> shorterPermutations = permutations(shorter);

I n fin e , a g g i u n g ia m o a t u t t e le p e r m u t a z i o n i d e lla p a r o la p iù b r e v e il c a r a tte r e


p r e c e d e n te m e n te escluso:

for (String s : shorterPermutations)


{
result.add(word.charAt(i) + s);
}

C o m e sem p re, d o b b ia m o p re v e d e re u n caso speciale p e r gestire le s trin g h e p iù se m p lici. La


p iù se m p lic e s trin g a p o ssib ile è la s trin g a v u o ta , c h e h a u n ’u n ic a p e r m u ta z io n e : se stessa.
E c c o la classe Permutations c o m p le ta .

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

Fornisce tutte le permutazioni della parola data.


@param word la stringa di cui calcolare le permutazioni
^return un vettore contenente tutte le permutazioni
*/
public static ArrayList<String> permutations(String word)
{
ArrayList<String> result = new ArrayList<String>();

// la stringa vuota ha un'unica permutazione: se stessa


if (word.lengthO == O)
{
result.add(word);
return result;
}
else
{
// effettua un ciclo esaminando tutti i caratteri della stringa
for (int i = 0; i < word.length(); i++)
{
// elimina il carattere i-esimo
String shorter = word.substring(0, i) + word.substring(i + l);

// genera tutte le permutazioni della parola più breve


ArrayList<String> shorterPermutations = permutations(shorter);

// aggiungi il carattere escluso all'inizio di ciascuna


// permutazione della parola più breve
for (String s : shorterPermutations)
{
result.add(word.charAt(i) + s);
}
}
// restituisci tutte le permutazioni
return result;

Esecuzione del programma:


eat
età
aet
ate
tea
tae

C o n f r o n ta te le classi Permutations e Triangle, c h e fu n z io n a n o s e c o n d o lo stesso p rin c ip io :


q u a n d o e la b o ra n o dati co m p lessi in ingresso, p e r p rim a cosa ris o lv o n o il p ro b le m a relativ o
a dati p iù se m p lic i, q u in d i c o m b in a n o il risu lta to o tte n u to c o n u lte r io ri e la b o ra z io n i,
f o rn e n d o i risu lta ti p e r i d ati p iù co m p lessi. N o n c ’è dav v ero alc u n a p a rtic o la re co m p lessità
in q u esta p ro c e d u ra , se p e n s a te alla so lu z io n e so lta n to a q u e s to livello: d ie tro le q u in te .
652 C a p it o l o 12

À 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

possibile d e te rm in a re se il p ro g ra m m a , ec co u n altro ese m p io p iù sem plice: è im possibile, dal p u n to di vista logico,


elab o ra n d o q u e ll’ingresso, te rm in a . Il p o te te usare u n p ro g ra m m a c h e c o n ta realizzare u n p ro g ra m m a c h e sia in
p ro b le m a della te rm in a z io n e affer­ le p aro le p e r c o n ta re le p aro le c h e si g ra d o di v erific are se u n p ro g ra m m a
m a, p e rò , c h e è im p o ssib ile trovare tro v an o nel suo stesso co d ice so rg en te. qualsiasi te rm in a c o n u n p a rtic o la re
u n u n ic o a lg o ritm o dec isio n ale ch e Se K o ttie n e da H l a risp o sta c h e d a to in ingresso.
fu n z io n i c o n qualsiasi p ro g ra m m a e R te rm in a q u a n d o v ie n e a p p lic a to È triste sap ere c h e e s isto n o H-
qualsiasi d ato in ingresso. N o ta te ch e a se stesso, esso è p ro g ra m m a to p e r rtnti alla capacità di ela b o ra z io n e dei
n o n p o te te se m p lic e m e n te eseg u ire il e n tra re in u n ciclo in fin ito ; a ltrim e n ti c o m p u te r: esisto n o p ro b le m i ai quali
p ro g ra m m a P c o n il d a to di ingresso K te r m in a . In Ja v a, il p ro g ra m m a n e s s u n p r o g ra m m a p e r c o m p u te r ,
1 p e r risp o n d e re alla d o m a n d a : se il p o tre b b e asso m ig liare a q u e sto : p e r q u a n to in g e g n o so , p u ò dare u n a
p ro g ra m m a rim a n e in e se c u z io n e p e r risposta.
1000 g io rn i, n o n sapete se si trova in public class Killer G li in f o rm a tic i te o r ic i s ta n n o
u n ciclo in fin ito , forse basta so ltan to { lav o ra n d o a m o lte altre ric e rc h e c h e
public static void main(...)
aspettare u n altro g io r n o p e r v ed e rlo rig u ard an o la n atu ra deU’elab o razio n e.
{
te rm in a re . String r = legge i d a ti U n a d o m a n d a im p o r ta n te , a n c o ra
U n tale “ v erific a to re di te rm i- HaltChecker checker = new senza risposta, rig u ard a i p ro b lem i c h e
n a z io n e ” , se p o te sse essere s c ritto , HaltCheckerO ; ric h ie d o n o u n te m p o di e la b o ra z io n e
p o tr e b b e essere u tile a n c h e p e r la if (checker.check(r, r))
tro p p o lu n g o p e r essere risolti: q u esti
c o rre z io n e degli esercizi di p ro g ra m ­ {
p ro b le m i p o tre b b e ro essere in trin s e ­
while (true)
m azio n e. U n d o c e n te p o tre b b e usarlo { 11 ciclo infinito c a m e n te difficili, n el q u al caso n o n
c o n gli elab o rati d eg li s tu d e n ti p e r } av reb b e senso ce rcare di id e n tifica re
v ed ere se e n tra n o in u n ciclo in fin ito } a lg o ritm i m ig lio ri. Q u e s te ric e rc h e
c o n u n p artico la re d ato di ingresso, else te o r ic h e p o s s o n o avere im p o r ta n ti
e v ita n d o di co n tro llarli p iù a fo n d o . {
ap p lica zio n i p ratich e. A d esem p io , al
return;
T uttavia, c o m e d im o s trò T u rin g , u n g io rn o d ’o g g i n essu n o sa se gli sc h em i
}
tale p ro g ra m m a n o n p u ò essere scritto. di c ritto g ra fia p iù c o m u n i p o ssa n o
}
La sua d im o s tra z io n e è in g e g n o sa e } essere violati sc o p re n d o n u o v i a lg o rit­
abbastanza sem plice. m i: sapere c h e n o n esisto n o alg o ritm i
S u p p o n ia m o c h e il “ v erific ato re O ra c h ie d e te v i: q u a l è la risp o sta d el v e lo c i p e r v io la re u n a p a r tic o la re
di te rm in a z io n e ” {halt checker) esista v e rific a to re di te rm in a z io n e q u a n d o c o d ific a p o tr e b b e farci s e n tire p iù
e c h ia m ia m o lo H . A p a rtire da H , gli v ie n e c h ie sto se K te rm in a n el a n o stro a g io q u a n d o p e n sia m o alla
sv ilu p p iam o u n altro p ro g ra m m a , il caso in c u i gli v e n g a fo rn ito K c o m e sicurezza della critto g ra fia
p ro g ra m m a “ k iller” , K, c h e svolge la d a to di in g resso ? F o rse sc o p re c h e
se g u e n te elab o raz io n e: il su o d ato di K e n tra in u n ciclo in fin ito c o n tale
ingresso è u n a strin g a c h e c o n tie n e ingresso. M a , a tte n z io n e : q u e s to n o n
il c o d ic e so rg e n te di u n p ro g ra m m a p u ò essere esatto , p e rc h é sig n ific h e ­
R , q u in d i esso applica il v e rific a to re re b b e c h e checker.check(r, r) re stitu ­
di te r m in a z io n e H al p r o g ra m m a isce false q u a n d o r è il c o d ic e so r­
R c o n la strin g a di ing resso R , cio è g e n te di K. C o m e p o te te fa c ilm e n te «s s^
verifica se il p ro g ra m m a R si arresta v ed e re, in tal caso il m e to d o main del
q u a n d o gli v ie n e fo rn ito il su o stesso p ro g ra m m a k ille r te rm in a , p e r cu i K
c o d ic e so rg e n te c o m e d a to d i in g res­ n o n è e n tra to in u n ciclo in fin ito . C iò
so. P o tre b b e su o n a re stran o c h e a u n m o stra c h e K deve te rm in a re q u a n ­
p ro g ra m m a v en g a fo rn ito in ingresso d o analizza se stesso, p e r cu i checker,
il p ro g ra m m a stesso, m a c iò n o n è check(r, r) d o v re b b e restitu ire tru e.
im possibile: ad esem p io , il c o m p ila ­ M a , in tal caso, il m e to d o main d el
to re Java è sc ritto in Java e lo p o te te p ro g ra m m a k iller n o n te rm in a : e n tra
usare p e r c o m p ila re se stesso. O p p u re , in u n ciclo in fin ito . C iò d im o stra c h e
654 C a p it o l o 12

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?

Per far pratica


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 E 1 2 .1 4 e E 1 2 .1 5 , al t e r m in e d e l c a p ito lo .

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

In c h e m o d o i d ia g ra m m i sin ta ttic i ci a iu ta n o a ca lc o lare il v alo re d e ll’alb ero ? Se g u a rd a te


gli a lb e ri s in ta ttic i, v e d re te c h e essi ra p p re s e n ta n o m o lto a c c u r a ta m e n te l’o r d in e di
e s e c u z io n e d e lle o p e ra z io n i. N e l p r im o alb ero 4 e 5 d e v o n o essere m o ltip lic a ti, q u in d i il
risu lta to d ev e essere s o m m a to a 3. N e l s e c o n d o a lb e ro 3 e 4 d e v o n o essere so m m a ti, p e r
p o i m o ltip lic a re il risu lta to p e r 5.
A lla fin e d i q u e s to p a ra g ra fo tro v e re te !’im p le m e n ta z io n e della classe Evaluator c h e
v alu ta q u e s to tip o di esp ressio n i. U n o g g e tto Evaluator usa la classe ExpressionTokenizer,
c h e s c o m p o n e u n a strin g a in e le m e n ti, d e tti token: n u m e ri, o p e r a to ri e p a re n te si (p er
se m p lic ità , a c c e ttia m o c o m e n u m e ri so lta n to n u m e ri in te ri p o sitiv i e n o n c o n s e n tia m o
la p re se n z a di spazi n e i d ati in in g resso ).
Q u a n d o si in v o c a nextToken, l’e le m e n to successivo in in g resso v ie n e re s titu ito so tto
fo rm a d i strin g a . F o rn ia m o a n c h e u n altro m e to d o , peekToken, c h e c o n s e n te di isp e z io n a re
l’e le m e n to su ccessivo sen za estrarlo. P e r c a p ire p e rc h é q u e s to m e to d o sia n ec essario ,
c o n s id e ra te il d ia g ra m m a s in ta ttic o di u n te rm in e : se l’e le m e n to successiv o è * o /
c o n tin u e r e te a m o ltip lic a re o d iv id e re fa tto ri, m e n tre se è u n c a ra tte re d iv erso , c o m e + o -,
d o v re te fe rm a rv i senza estra rlo v e ra m e n te , in m o d o c h e v en g a a n a liz za to su ccessiv am en te.
P er calc o lare il v alo re di u n ’e sp re ssio n e re a liz z ia m o tre m e to d i: getExpressionValue,
getTermValue e getFactorValue. P e r p rim a cosa il m e to d o getExpressionValue in v o ca getTermValue
p e r o tte n e r e il v alo re d el p r im o te r m in e d e ll’esp ressio n e, q u in d i v erific a se l’e le m e n to
su ccessivo è u n c a ra tte re + o -: in tal caso in v o c a getTermValue di n u o v o e so m m a o so ttra e
il v alo re c h e o ttie n e .

public int getExpressionValueO


{
int value = getTermValue();
boolean done = false;
while (!done)
{
String next = tokenizer.peekToken();
if ("+".equals(next) || "-".equals(next))
R ic o r s io n e 657

{
tokenizer.nextTokenO; // ignora "+" o "-"
int value2 = getTermValue();
if ('■+".equals(next)) { value = value + value2; }
else { value = value - value2; }
}
else
{
done = true;
}
}
return value;
}

Il m e t o d o getTermValue i n v o c a getFactorValue a lio s te s s o m o d o , m o l t i p l i c a n d o o d i v i d e n d o


i v a lo r i d e i fa t t o r i.
I n f in e , il m e t o d o getFactorValue v e r if ic a se T e le m e n t o s u c c e s s i v o è u n n u m e r o o se
in iz ia c o n u n a p a r e n te s i t o n d a a p e r ta . N e l p r i m o c a s o il v a lo r e r e s tit u it o è s e m p l i c e m e n t e il
v a lo r e d e l n u m e r o , m e n t r e n e l s e c o n d o c a s o il m e t o d o getFactorValue in v o c a r ic o r s iv a m e n t e
il m e t o d o getExpressionValue. In tal m o d o , i tre m e t o d i r is u lt a n o e s s e r e m u t u a m e n t e
r ic o r s iv i.

public int getFactorValueO


{
int value;
String next = tokenizer.peekToken();
if ("(".equals(next))
{
tokenizer.nextTokenO; // ignora "("
value = getExpressionValueO;
tokenizer.nextTokenO; // ignora ")"
}
else
{
value = Integer.parselnt(tokenizer.nextTokenO);
}
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:

getExpressionValue invoca getTermValue


• getTermValue invoca getFactorValue
• getFactorValue legge ( dalla stringa d’ingresso
• getFactorValue invoca getExpressionValue
• getExpressionValue restituisce il valore 7, dopo aver letto 3 +4 . ^cco
l’invocazione ricorsiva
• getFactorValue legge ) dalla stringa d’ingresso
• getFactorValue restituisce 7
• getTermValue legge * e 5 dalla stringa d’ingresso e restituisce 35
getExpressionValue restituisce 35
658 C a p it o l o 12

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

Una classe che calcola il valore di una espressione aritmetica.


*/
public class Evaluator
{
private ExpressionTokenizer tokenizer;

/**
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

public int getTermValueO


{
int value = getFactorValue();
boolean done = false;
while (!done)
{
String next = tokenizer.peekToken();
if ("*".equals(next) || '7".equals(next))
{
tokenizer.next!oken();
int value2 = getFactorValue();
if equals(next)) { value = value * value2; }
else { value = value / value2; }
}
else
{
done = true;
}
}
return value;
}

Valuta il successivo fattore nell'espressione,


^return il valore del fattore
*/
public int getFactorValueO
{
int value;
String next = tokenizer.peekToken();
if ("(".equals(next))
{
tokenizer.nextTokenO; // ignora "("
value = getExpressionValueO;
tokenizer.nextTokenO; // ignora ")"
}
else
{
value = Integer.parseInt(tokenizer.nextTokenO);
}
return value;
}
}

File ExpressionTokenizer.java

Questa classe scompone un'espressione in elementi


(detti token): numeri, parentesi tonde e operatori.
*/
public class ExpressionTokenizer
{
private String input;
private int start; // l'inizio del token attuale
private int end; // la posizione successiva all'ultima del token attuale
660 C a p it o l o 12

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

public class ExpressionCalculator


{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter an expression: '');
String input = in.nextLine();
Evaluator e = new Evaluator(input);
int value = e.getExpressionValue();
System.out.println(input + "=" + value);
}
}

Esecuzione del programma


Enter an expression; 3+4*5
3+4*5=23

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 .

prendere in esame altri candidati Il b a c k t r a c k in g p u ò e s s e r e u s a t o p e r r is o lv e r e c r u c iv e r b a , p e r u s c ir e d a la b ir in t i o ,


alla soluzione fìnale. p iù in g e n e r a le , p e r tr o v a re s o l u z i o n i d i p r o b l e m i v i n c o la t i d a r e g o l e . P er r is o lv e r e u n
p r o b le m a m e d i a n t e b a c k t r a c k in g s e r v o n o d u e c o s e :

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 .

public class PartialSolution


{
private Queenf] queens;

public int exam in eO { . . . }


public PartialSolution[] extend() { . . . }

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:

public int examineO


{
for (int i = 0; i < queens.length; ì -h -)
{
for (int j = i + 1; j < queens.length; j++)
{
if (queens[i].attacks(queens[j])) { return ABANDON; }
}
}
if (queens.length == NOUEENS) { return ACCEPT; }
else { return CONTINUE; }
}

11 metodo extend riceve una soluzione parziale e ne fa otto copie, aggiungendo a ciascuna
una nuova regina in una diversa colonna:

public PartialSolution[] extend()


{
// genera una nuova soluzione per ciascuna colonna
PartialSolution[] result = new PartialSolution[NOUEENS];
for (int i = 0; i < result.length; i++)
{
int size = queens.length;

// la nuova soluzione ha una riga in più di questa


result[i] = new PartialSolution(size + l);

// copia questa soluzione nella nuova


for (int 3 = 0; j < size; j++)
{
result[i].queens[j] = queens[j];
}

// aggiunge la nuova regina nella colonna di indice i


result[i].queens[size] = new Oueen(size, i);
}
return result;
}

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:

(riga^ —rigaj)/(colonna^ - colonna^) = ± 1


(riga2 - riga^) = ± (colonna^ - colonna,)
Iriga^ - riga, | = | colonna^ - colonna, |
664 C a p it o l o 12

(D

4% ^
■ « ■ ■ W i i n i i « ■ I W D □ W Q ■ W B ^

Figura 6 Backtracking nel problema della quattro regine

O sse rv a te c o n a tte n z io n e il m e to d o solve n e l p ro g ra m m a EightOueens p re s e n ta to n e l


se g u ito : è u n a se m p lic e tra d u z io n e d ello p s e u d o c o d ic e c h e a b b ia m o p re s e n ta to p e r la
te c n ic a di b a c k tra c k in g g e n e ric a . N o ta te c o m e in q u e s to m e to d o n o n ci sia n e ssu n c e n n o
sp e c ific o al p ro b le m a d elle o tto re g in e : fu n z io n a c o n qualsiasi tip o di so lu z io n e p arziale
e p e r qualsiasi p ro b le m a , p u r c h é v e n g a n o d e fin iti o p p o r tu n a m e n te i m e to d i examine e
extend (si v ed a, in p ro p o sito , l’E se rc iz io E 1 2 .2 2 ).
La F ig u ra 6 m o stra il m e to d o solve in a z io n e p e r riso lv e re il p ro b le m a d elle q u a ttro
re g in e , cio è il p ro b le m a discusso fin o ra m a, p e r sem p lic ità , lim ita to al caso di u n a scacch iera
4 x 4 . P a rte n d o da u n a sc ac ch ie ra v u o ta (in alto ), v e n g o n o g e n e ra te q u a ttro so lu z io n i

parziali, c o n u n a re g in a nella rig a 1 ® . Q u a n d o la re g in a si trova n ella c o lo n n a 1 esisto n o


q u a ttro s o lu z io n i parziali c h e h a n n o u n ’u lte r io re re g in a nella rig a 2 © , d u e d elle q u ali
v e n g o n o a b b a n d o n a te im m e d ia ta m e n te . O g n u n a d elle altre d u e g e n e ra q u a ttro n u o v e
so lu z io n i parziali c o n tre re g in e sulla sc a c c h ie ra (® e ® ), m a v e n g o n o tu tte a b b a n d o n a te ,
tra n n e u n a . Q u e lla c h e n o n è stata a b b a n d o n a ta v ie n e estesa g e n e ra n d o q u a ttro so lu z io n i,
ciasc u n a d elle q u ali h a q u a ttro re g in e sulla sc a c ch ie ra , m a v e n g o n o tu tte a b b a n d o n a te
(D.A q u e s to p u n to l ’a lg o ritm o e ffe ttu a u n ’a z io n e di b a c k tra c k in g , c io è “ to r n a sui p ro p ri
passi” , d e c id e n d o c h e n o n è p o ssib ile tro v are u n a so lu z io n e c h e ab b ia u n a re g in a n ella
p o siz io n e a l e p ro c e d e n d o c o n l’e ste n sio n e della so lu z io n e p arziale c h e ha u n ’u n ic a reg in a
n ella p o siz io n e bi (i successivi passi d e ll’a lg o ritm o n o n so n o m o stra ti in fig u ra).
E se g u e n d o il p ro g ra m m a si o ttie n e u n e le n c o di 9 2 so lu z io n i, c o m p re sa q u ella già
vista nella F ig u ra 5. L’E serc izio E 1 2 .2 3 vi c h ie d e di e lim in a re d a ll’e le n c o q u e lle so lu z io n i
c h e so n o s e m p lic e m e n te u n a ro ta z io n e o u n a rifle ssio n e di u n ’altra so lu z io n e .
R ic o r s io n e

File PartialSolution.java
import java.util.Arrays;

Una soluzione parziale del rompicapo delle otto regine.


*/
public class PartialSolution
{
private Queen[] queens;
private static final int NOUEENS = 8;

public static final int ACCEPT = 1;


public static final int ABANDON = 2;
public static final int CONTINUE = 3;

Costruisce una soluzione parziale di dimensione assegnata.


@param size la dimensione assegnata
*/
public PartialSolution(int size)
{
queens = new Oueen[size];
}

/**
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; }
}

Restituisce tutte le estensioni di questa soluzione parziale.


@return un array con le soluzioni parziali che estendono questa.
*/
public PartialSolution[] extend()
{
// genera una nuova soluzione per ciascuna colonna
PartialSolution[] result = new PartialSolution[NQUEENS];
for (int i = 0; i < result.length; i++)
{
int size = queens.length;

// la nuova soluzione ha una riga in più di questa


result[i] = new PartialSolution(size + l);
666 C a p it o l o 12

11 copia questa soluzione nella nuova


for (int j = 0; j < size; j++)
{
result[i].queens[j] = queens[j];
}

// aggiunge la nuova regina nella colonna di indice i


result[i].queens[size] = new Oueen(size, i);
}
return result;

public String to StringO { return Arrays.toString(queens);


}

File Queen.java

Una regina nel rompicapo delle otto regine.


*/
public class Oueen
{
private int row;
private int column;

Costruisce una regina nella posizione assegnata.


@param r la riga
@param c la colonna
*/
public Oueen(int r, int c)
{
row = r;
column = c;
}

/**
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);
}

public String toStringO


{
return "" + "abcdefgh".charAt(column) + (row + l) ;
}
R ic o r s io n e 667

File EightQueen.java

Risolve il rompicapo delle otto regine usando il backtracking.


*/
public class EightOueens
{
public static void main(String[] args)
{
solve(new PartialSolution(O));
}

/**
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);
}
}
}
}

Esecuzione del programma


['ai', ’e2', ’h 3 ‘, 'f4'. 'CS', ■g6', 'b7‘, 'd8']
['al', •f2', ■h3', 'c4'. ■g5', 'd6', 'br, ■e8']
['al'. ■g2’, ■d3', 'f4'. 'hS', 'be', 'e?', ■c8']

i'fi'. ’a2', 'e3', 'b4'. 'hS', 'c6', 'g7', ■d8']

i'hl'. ■c2', 'a3', 'f4'. 'bS', 'e6', •g7', 'd8']


['hi'. ’d2', 'a3', 'c4'. 'f5', 'be', ■g7'. '0 8 ']

(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 .

Es^empi coniiilfiti.U,2 ......... .....................™ _______________


LeTorridi Hanoi
Descrizione del problema. Il r o m p ic a p o d elle “ T o rri di H a n o i” si g io c a c o n u n a
ta v o le tta su c u i s o n o infissi tre p io li v erticali (peg), c ia sc u n o ca p a c e di a c c o g lie re u n a pila
di d isc h i c irc o la ri, b u c a ti c e n tr a lm e n te p e r infilarsi n e l p io lo . I d isc h i h a n n o d ia m e tri
tu tti d iversi e all’in iz io d el g io c o tu tti i d isc h i s o n o in filati n e l p r im o p io lo in o rd in e di
d im e n s io n e d e c re s c e n te v erso l’alto (c io è il p iù g ra n d e è in basso e il p iù p ic c o lo è in
cim a, d isp o sti o rd in a ta m e n te , c o m e si p u ò v e d e re n ella F ig u ra 7).
L’o b ie ttiv o è q u e llo di sp o stare tu tti i d isc h i sul te rz o p io lo . Si p u ò sp o stare u n so lo
disco p e r volta, d a u n p io lo a u n altro, sen za p o te rlo p o siz io n a re so p ra u n d isco p iù p ic co lo .
La le g g e n d a d ic e c h e in u n te m p io , p re s u m ib ilm e n te n ella c ittà di H a n o i, esiste u n a
s tr u ttu ra di q u e s to tip o c o n se ssa n ta q u a ttro d isc h i d ’o ro c h e v e n g o n o c o n tin u a m e n te
sp o stati dai m o n a c i se g u e n d o le re g o le q u i in d ic a te : q u a n d o a v ra n n o sp o sta to tu tti i d isch i
sul te rz o p io lo , il m o n d o fin irà.
A iu tia m o i m o n a c i sc riv e n d o u n p ro g ra m m a c h e v isu alizzi le m o sse da eseg u ire,
n e ll’o rd in e c h e c o n s e n ta di riso lv e re il ro m p ic a p o .

Figura 7
LeTorri di Hanoi

C o n s id e r ia m o il p ro b le m a rela tiv o allo s p o s ta m e n to di d d isc h i dal p io lo al p io lo


d o v e P j e p , so n o i p io li n u m e ra ti c o n 1, 2 o 3, c o n Pj ^ p^. D a to c h e 1 + 2 + 3 = 6,
p o ssia m o ca lc o lare l’in d ic e d e l te rz o p io lo , d ati Pj e p^, c o m e p^ = 6 - Pj - p^.
P o ssiam o sp o stare i d isc h i in q u e s to m o d o :

• sp o stia m o d a Pj a p^ i r/ — 1 d isch i c h e si tro v a n o p iù in alto ;


• sp o stia m o da Pj a p^il d isc o c h e si trovava in fo n d o alla pila di d d isch i;
• sp o stia m o da p^ a p , i — 1 d isch i c h e e ra n o stati p a rc h e g g ia ti te m p o ra n e a m e n te .

La p rim a e la te rz a fase d e v o n o essere g estite ric o rsiv a m e n te , m a rig u a rd a n o lo sp o sta m e n to


di u n n u m e ro di d isch i in fe rio re (di u n ’u n ità ), q u in d i,p r im a o p o i,la r ic o rs io n e te rm in e rà .
N o n è difficile tra d u rre in Java q u e s to a lg o ritm o . N e lla se c o n d a fase v isu a liz zia m o , a
b e n e fic io d ei m o n a c i, la m o ssa da c o m p ie re , in u n f o rm a to c o m e q u e sto :
R ic o r s io n e 669

Move disk from peg I to 3

FileTowersOfHanoilnstructions.java

Visualizza le istruzioni per risolvere il rompicapo delle Torri di Hanoi.


*/
public class TowersOfHanoiInstructions
{
public static void main(String[] args)
{
move(5, 1, 3);
}

/**
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);
}

Esecuzione del programma


Move disk from peg I to 3
Move disk from peg I to 2
Move disk from peg 3 to 2
Move disk from peg I to 3
Move disk from peg 2 to I
Move disk from peg 2 to 3
Move disk from peg I to 3
Move disk from peg I to 2
Move disk from peg 3 to 2
Move disk from peg 3 to I
Move disk from peg 2 to I
Move disk from peg 3 to 2
Move disk from peg I to 3
Move disk from peg I to 2
Move disk from peg 3 to 2
Move disk from peg I to 3
Move disk from peg 2 to I
Move disk from peg 2 to 3
Move disk from peg I to 3
Move disk from peg 2 to I
Move disk from peg 3 to 2
0/U L A P I T O L O 11

Move disk from peg 3 to I


Move disk from peg 2 to I
Move disk from peg 2 to 3
Move disk from peg I to 3
Move disk from peg I to 2
Move disk from peg 3 to 2
Move disk from peg I to 3
Move disk from peg 2 to I
Move disk from peg 2 to 3
Move disk from peg I to 3

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;

Una torre contenente dischi nel rompicapo delle Torri di Hanoi.


*/
public class Tower
{
private ArrayList<Integer> disks;

/**
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

Aggiunge un disco in cima a questa torre.


@param size la dimensione del disco da aggiungere
*/
public void add(int size)
{
if (disks.size() > O && disks.get(disks.size() - i) < size)
{
throw new IllegalStateException(''Disk is too large");
}
disks.add(size);
}

public String to StringO { return disks.toStringO; }

Un oggetto TowersOfHanoi che rappresenta un rompicapo ha tre torri:

public class TowersOfHanoi


{
private Tower[] towers;

public TowersOfHanoi(int ndisks)


{
towers = new Tower[3];
towers[o] = new Tower(ndisks);
towers[l] = new Tower(O);
towers[2] = new Tower(O);
}

} * * ’

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);
}

E s e g u e n d o il p ro g ra m m a , si o ttie n e la v isu a liz z a z io n e d i q u e s te in fo rm a z io n i;

[[5, 4, 3, 2], [], [1]]


[[5, 4, 3], [2], [1]]
[[5, 4, 3], [2, 1], []]
[[5, 4], [2, 1], [3]]
[[5, 4, 1], [2], [3]]
[[5, 4, 1], [], [3, 2]]
[[5, 4], [], [3, 2, 1]]
[[5], [4], [3, 2. 1]]
[[5], [4. 1], [3, 2]]
[[5, 2], [4. 1], [3]]
[[5, 2, 1], [4], [3]]
[[5, 2, 1], [4. 3], []]
[[5, 2], [4, 3], [1]]
[[5], [4, 3, 2], [1]]
[[5], [4. 3, 2, 1], []]
[[], [4, 3, 2, 1], [5]]
[[1], [4, 3, 2], [5]]
[[1], [4, 3], [5, 2]]
[[], [4, 3], [5, 2, 1]]
[[3], [4], [5. 2, 1]]
[[3], [4, 1], [5. 2]]
[[3, 2], [4, 1], [5]]
[[3, 2, 1], [4], [5]]
[[3, 2, 1], [], [5, 4]]
[[3, 2], []. [5, 4, 1]]
[[3], [2], [5. 4, 1]]
[[3], [2, 1], [5, 4]]
[[], [2, 1], [5, 4, 3]]
[[1], [2], [5. 4. 3]]
[[1], [], [5, 4, 3, 2]]
[[], [], [5. 4. 3. 2, 1]]

D e c is a m e n te m e g lio ; o ra si p u ò v e d e re c o m e si sp o sta n o i d isc h i e si p u ò v e rific a re


fa c ilm e n te c h e tu tte le m o sse e ffe ttu a te sia n o valide, c o n tro lla n d o c h e il c o n te n u to di
o g n i lista sia se m p re d e c re sc e n te .
C o m e si p u ò n o ta re , p e r riso lv e re il r o m p ic a p o c o n 5 d isc h i s e rv o n o 31 = 2® - 1
m osse, q u in d i p e r riso lv e re il r o m p ic a p o c o n 6 4 d isc h i, q u e llo d ei m o n a c i, o c c o r ro n o
2>'‘>— 1 = 1 8 4 4 6 7 4 4 0 7 3 7 0 9 5 5 1 6 1 5 m osse. S e i m o n a c i rie s c o n o a sp o stare u n d isc o al
se c o n d o , p e r te rm in a r e il la v o ro s e rv o n o circ a 5 8 5 m ilia rd i di an n i. D a to c h e Ia T e rra si
è fo rm a ta circa 4 .5 m ilia rd i d i a n n i fa, n o n d o b b ia m o p re o c c u p a rc i m o lto d el fa tto c h e
il m o n d o finisca q u a n d o i m o n a c i c o m p le te r a n n o il ro m p ic a p o .

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

5 Visualizza una soluzione del rompicapo delle Torri di Hanoi.


6 */
7 public class TowersOfHanoiDemo
8 {
9 public static void main(String[] args)
10 {
11 final int NDISKS = 5;
12 TowersOfHanoi towers = new TowersOfHanoi(NDISKS);
13 towers.move(NDISKS, 0, 2);
14 }
2L }

File TowersOfHanoi.java
import java.util.Arrays;

Un rompicapo delle Torri di Hanoi con tre torri.


*/
public class TowersOfHanoi
{
private Tower[] towers;

Costruisce un rompicapo nel quale la prima torre ha il numero di dischi dato.


@param ndisks il numero di dischi
*/
public TowersOfHanoi(int ndisks)
{
towers = new Tower[3];
towers [o] = new Tower(ndisks);
towers [ l ] = new Tower (o);
towers [2] = new Tower (o);
}

Sposta 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 void move(int disks, int from, int to)
{
if (disks > 0)
{
int other = 3 - from - to;
move(disks - I, from, other);
towers[to].add(towers[from].remove());
System.ou t.printIn(Arrays .toString(towers));
move(disks - I, other, to);
}
}
674 C a p it o l o 12

Riepìlogo degli obiettivi dì apprendimento


Comprendere il flusso d'esecuzione di un'elaborazione ricorsiva
• U n ’e la b o ra z io n e ricorsiva riso lv e u n p ro b lem a u san d o la s o lu z io n e del p rob lem a stesso nel
caso di dati di in gresso p iù sem p lici.
• P erch é una r ic o r s io n e te rm in i, d e v o n o esistere casi sp eciali p er i dati in in gresso p iù sem p lici.

Individuare i metodi ausiliari ricorsivi utili per risolvere un problema


• A v o lte è p iù se m p lice trovare una so lu z io n e ricorsiva d o p o aver a p p ortato una p ic co la m o ­
d ifica al p rob lem a o rig in a r io .

Confrontare l'efficienza di algoritmi ricorsivi e non ricorsivi


• A v o lte un a s o lu z io n e ricorsiva v ie n e eseg u ita m o lto più len ta m en te di una s o lu z io n e itera­
tiva del m e d e sim o p rob lem a, m a nella m a g g io r parte d ei casi la s o lu z io n e ricorsiva è so lta n to
p o c o più len ta.
• In m o lti casi una s o lu z io n e ricorsiva è p iù fa cile da capire e da realizzare co rr etta m e n te , ri­
sp etto a un a so lu z io n e iterativa.

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 .

La ricorsione mutua: l'esempio di un valutatore di espressione


• U n a r ic o r s io n e m u tu a è caratterizzata da un in sie m e di m e to d i c o o p e r a n ti c h e si in v o c a n o
l ’un l’altro r ip etu ta m en te.

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.

Esercizi di riepilogo e approfondimento


* R 1 2 . 1 . D e fin ite i se g u e n ti term in i:
a. r ic o rsio n e
b. itera zio n e
c. r ic o r s io n e in fin ita
d. m e to d o au siliario rico rsiv o

** R 1 2 .2 . D e lin e a te , senza im p lem en ta rla , una so lu z io n e ricorsiva p er trovare il valore m in im o in un


array.

k-k-k R 1 2 .3 . D e lin e a te , senza im p lem en ta rla , un a s o lu z io n e ricorsiva p er trovare il fe-esim o e le m e n to


più p ic c o lo in u n array. S u j^ e r im e n to : cercate gli e le m e n ti c h e so n o m in o r i d e ll’e le m e n to iniziale;
se q u esti so n o m , c o m e si p r o c ed e se k ^ m ? E s e k > m ?

** R 1 2 .4 . D e lin e a te , senza im p lem en ta rla , una s o lu z io n e ricorsiva p er ordinare un array di n u m eri.


S u ii^ e rim e n to : p er prim a cosa trovate il valore m in im o n e ll’array.*

* R 1 2 . 5 . D e lin e a te , senza im p lem en ta rla , un a so lu z io n e ricorsiva p er ordinare un array di n u m eri.


i n s e r i m e n t o ’, p er prim a cosa ord in ate il so tto-array p rivo d e ll’e le m e n to in iziale.
R ic o r s io n e 675

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 . 7 . N e l caso in cu i ri sia pari, m ig lio ra te l ’ese rc iz io p r e ce d e n te c a lc o la n d o x ” c o m e


P erch é qu esta so lu z io n e è d e c isa m en te p iù v e lo c e? S u g g e r im e n to : ca lco la te ^ ^^io24 en tram b i
i m o d i.

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 0 . N e l r o m p ica p o d e lle “ T orri di H a n o i” v isto nella s e z io n e E sem p i c o m p le ti 1 2 .2 in d i­


c h ia m o c o n m o sse(« ) il n u m er o di m o sse n ecessarie p er spostare n d isch i.T rovate una form u la c h e
esp rim a m osse(H ) in fu n z io n e di m osse(iJ — 1), p o i d im ostrate c h e m osse(n ) = 2" — 1.

R 1 2 . i l . D e lin e a te , senza im p lem en ta rla , un a s o lu z io n e ricorsiva p er gen erare tutti i so tto in s ie m i


d e ll’in sie m e { 1 , 2 , ..., n } .

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.

R l 2 .1 3 . S e g u ite passo d o p o passo l ’e s e c u z io n e del p rogram m a di v a lu ta zio n e d elle esp ression i,


v isto nel Paragrafo 1 2 .5 , q u a n d o analizza 3 - 4 + 5, 3 - (4 + 5), (3 - 4) * 5 e 3 * 4 + 5 * 6.

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 . 3 . S criv ete un m e to d o rico rsiv o c h e sc o m p o n g a in fattori p rim i u n n u m er o in tero n. Per


prim a cosa trovate un f a t t o r e ,/ p o i sc o m p o n e te r ic o rsiv a m en te in fattori n / f .

^ 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 . 5 . S criv ete u n m e to d o r ico rsiv o String reverse(String text) c h e inverta il c o n te n u to di una


stringa. A d e se m p io , reverse("Hello! ") restitu isce la stringa "!olleH". R e a liz za te una so lu z io n e ri­
corsiva e lim in a n d o il p r im o carattere, in v e rte n d o la stringa r im a n en te e c o m b in a n d o le d u e parti.

** E 1 2 .6 . R is o lv e te di n u o v o l ’e sercizio p r e ce d e n te usan d o, p erò , u n m e to d o au siliario rico rsiv o c h e


in v erte una sottostrin ga.

* E 1 2 . 7 . R e a liz za te itera tivam en te il m e to d o reverse d e ll’E se r d z io E l 2 .5 .

** 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

public static boolean find(String text, String str)

c h e v erifica se la stringa te x t c o n tie n e la stringa str. A d e se m p io , find("Mississippi”, ”sip") resti­


tu isce true. S u ^ e r i m e n t o : se il testo in izia c o n la stringa c h e state cerca n d o , avete fin ito ; a ltrim en ti,
co n sid era te la stringa c h e si o ttie n e e lim in a n d o il p r im o carattere.

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

public static int indexOf(String text, String str)

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.

E 1 2 .1 0 . U s a n d o la r ic o rsio n e, trovate l ’e le m e n to m assim o in un array. S u g g e r im e n to : trovate l ’e le ­


m e n to m assim o n el s o tto in s ie m e c h e c o n tie n e tutti gli e le m e n ti d e ll’array tranne l ’u ltim o ; q u in d i,
c o n fro n ta te tale m a ssim o c o n il valore d e ll’u ltim o e le m e n to .

E 1 2 . i l . U s a n d o la r ic o rsio n e, ca lco la te la so m m a di tutti i valori presen ti in un array.

E 1 2 .1 2 . U sa n d o la r ic o rsio n e, ca lco la te l ’area di u n p o lig o n o . Id en tific a ten e una p o r z io n e trian ­


golare, c o m e in d ic a to nella figura, e sfruttate il fatto c h e u n tr ia n g o lo c o n v ertici (x j, y i), (x 2, y2)
e f e » Y i) ha area:

\x\y2 + X 2 Y3 + - nx2 - 72^3 - y3^l|


2

E 1 2 .1 3 . Il m e to d o se g u e n te p er il c a lc o lo della rad ice quadrata era n o t o a n c h e agli a n tic h i g r ec i.


D a to u n v alore x > 0 e u n valore g c a n d id a to a essere la rad ice quadrata di x , il va lo re (g + x / g ) /
2 è u n ca n d id a to m ig lio r e d i S c r i v e t e u n m e to d o au silia rio r ic o rsiv o , squareRootGuess(double x,
double g): S e ^ è circa u g u a le a x r estitu is c e ^ , a ltrim en ti r estitu isce il valore r estitu ito dal m e to d o
squareRootGuess in v o c a to c o n il c a n d id a to m ig lio r a to u sa n d o la fo r m u la d escritta in p r e ce d e n z a .
S c r iv e te, p o i, il m e to d o public static squareRoot(double x) c h e usi tale m e to d o au siliario.

E 1 2 .1 4 . R e a liz za te una classe SubstringGenerator c h e g e n e r i tu tte le so tto strin g h e di una stringa.


A d e se m p io , le so tto strin g h e della stringa "rum” s o n o q u este sette strin gh e:

rum , um ,
R ic o r s io n e 677

S u g g e r im e n to : d ap prim a trovate tu tte le so tto strin g h e c h e in iz ia n o c o n il p r im o carattere, c h e so n o


n se la strin ga ha lu n g h e zz a n; p o i, trovate le so tto str in g h e della stringa c h e si o ttie n e e lim in a n d o
il p r im o carattere.

E 1 2 . 1 5 . R e a liz z a te una classe SubsetGenerator c h e g e n e r i tu tti i so tto in s ie m i d ei caratteri di una


stringa. A d e se m p io , i so tto in s ie m i di caratteri della stringa "rum" so n o q u este o tto strin gh e:

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".

E 1 2 .1 6 . G en erate r ico rsiv a m en te tutti i m o d i in cu i u n vetto re p u ò essere su d d iv iso in una se q u e n ­


za di s o tto -v e tto r i n o n v u o ti. A d e se m p io , d ato il vetto re [1, 7 , 2, 9 ], d o v e te restituire il se g u e n te
v etto re di vettori:

[[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 . 1 7 . D a to un vetto re a di n u m er i in teri, trovate ric o rsiv a m e n te tutti i v etto ri di e le m e n ti di a


la cu i so m m a è u g u a le al n u m er o in tero «.

E 1 2 . 1 8 . Im m a g in a te di v o ler salire una scala di n grad in i (id en tifica ti da n u m er i naturali in se­


q u en za) e di p o te r fare u n g ra d in o o d u e a o g n i passo. E len ca te ric o rsiv a m e n te tu tte le possib ili
se q u e n z e di grad in i. A d e se m p io , se n vale 5, le se q u e n z e p o ssib ili son o:

[1, 2, 3, 4, 5], [1, 3, 4, 5], [l, 2, 4, 5], [l, 2, 3, 5], [l, 4, 5]

E 1 2 . 1 9 . R is o lv e te n u o v a m en te l ’ese rc iz io p r e ce d e n te n e ll’ip o te si c h e a o g n i passo si p ossa n o salire


fin o a k grad in i.

E 1 2 .2 0 . D a to u n p r e zz o so tto fo r m a di n u m e r o in tero , e le n c a te u sa n d o la r ic o r s io n e tu tti i


m o d i p o ssib ili p er pagarlo c o n b a n c o n o te da $ 1 0 0 , $ 2 0 , $ 5 e $ 1 , sen za c h e sia n o p resen ti m o d i
r ip etu ti.

E 1 2 . 2 1 . M ig lio ra te il valu tatore di esp ression i del Paragrafo 1 2 .5 in m o d o c h e possa gestire l ’o p e ­


ratore %e l ’o p eratore di “ e le v a m e n to a p o te n z a ” ''.A d e se m p io , 2 '' 3 vale 8. C o m e in m atem atica,
l ’e le v a m e n to a p o te n z a d e v e avere la p rece d e n z a sulla m o ltip lic a zio n e : 5 * 2 '' 3 vale 40 .

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.

E 1 2 .2 3 . M ig lio ra te il p rogram m a c h e riso lv e il ro m p ica p o d e lle o tto r eg in e in m o d o c h e n o n


v isu a lizzi s o lu z io n i c h e sian o ro ta zio n i o riflession i di so lu z io n i visu alizzate in p reced en za . 11 p ro­
gra m m a d e v e visualizzare d o d ic i s o lu z io n i.

E 1 2 .2 4 . M ig lio ra te il p rogram m a c h e risolve il ro m p ica p o d e lle o tto r eg in e in m o d o c h e le s o lu ­


z io n i v e n g a n o scritte in u n file H T M L , u san d o tab elle c o n sfo n d o b ia n co e n ero p er le sca cch iere
e il carattere U n ic o d e "\u2655' p er le regin e.
678 C a p it o l o 12

E l 2 .2 5 . R e n d e t e più g e n era le il p rogram m a c h e risolve il r o m p ica p o d elle o tto r eg in e in m o d o


c h e risolva il p rob lem a p er n reg in e. 11 p rogram m a d ev e c h ied er e all’u te n te il valore di n e, p o i,
visualizzare le so lu z io n i

E 1 2 . 2 6 . U s a n d o il b ack track in g, scrivete u n p rogram m a c h e risolva ro m p icap i rappresentati da


a d d izio n i tra strin g h e di lettere, d o v e o g n i lettera d e v e essere sostitu ita da un a cifra, c o m e questi:

send + more = money


base + ball = games
kyoto + Osaka = tokyo

E 1 2 . 2 7 . 11 c a lc o lo rico rsiv o d ei n u m er i di F ib o n a cci p u ò essere a ccelerato in m o d o sig n ifica tiv o


te n e n d o traccia d ei valori c h e s o n o già stati calcolati: realizzate una n u ova v e rsio n e del m e to d o
fib u san d o questa strategia. O g n i volta c h e restitu ite u n n u o v o valore, m e m o r iz z a te lo an ch e in un
array ausiliario; p rim a di iniziare il c a lc o lo di un valore, co n su lta te !’array p er v ed ere se tale c a lc o lo
sia già stato e se g u ito . C o n fro n ta te il te m p o di e s e c u z io n e della v e rsio n e co sì m igliorata c o n le
realizzazion i o r ig in a li, ricorsiva e iterativa.

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

L’o rd in a m e n to è u n a d elle o p e ra z io n i p iù fre q u e n ti n e ll’e la b o ra z io n e d ei d ati: ad e sem p io ,


spesso c ’è la n ecessità di v isu alizzare u n a rray di d ip e n d e n ti in o rd in e alfa b e tic o o p p u re
in o rd in e di stip e n d io . In q u e s to c a p ito lo im p a re re te a lc u n i a lg o ritm i di o r d in a m e n to
e le te c n ic h e c h e c o n s e n to n o di c o n fro n ta re le lo ro p re sta z io n i.T a li te c n ic h e n o n so n o
u tili so lta n to p e r i m e to d i di o rd in a m e n to , m a a n c h e p e r l’analisi di m o lti altri a lg o ritm i.
D o p o aver o rd in a to u n array di e le m e n ti vi si p o sso n o fa c ilm e n te e ffe ttu a re ric e rc h e ,
p e r v e rific a re la p re se n z a di sp ecifici e le m e n ti. S tu d ie re m o l’a lg o ritm o di ricerca binaria,
c h e ese g u e u n a ric e rc a rap id a .
680 C a p it o l o 13

13.1 Ordinamento per selezione


In q u e s to p a ra g ra fo illu stre re m o u n a lg o ritm o di o rd in a m e n to , il p r im o di q u elli c h e
v e d re m o . U n algoritmo di ordinamento {sorting algorithm) d is p o n e gli e le m e n ti di u n a
ra c c o lta di d ati in m o d o ch e, al te rm in e , siano m e m o riz z a ti in q u a lc h e o rd in e specifico. P er
r im a n e re su e se m p i se m p lic i, c o n s id e re re m o P o rd in a m e n to di u n arra y di n u m e ri in te ri,
p rim a d i passare a o rd in a re s trin g h e o d ati p iù co m p lessi. C o n s id e ra te il s e g u e n te array a:

[0][1][2][3][4]

11 9 17 5 12

U n a p rim a , e le m e n ta re fase c o n siste nella ric e rc a d e ll’e le m e n to m in im o , c h e in q u e s to


L'algoritmo di ordinamento
caso è 5 e si tro v a in a [ 3 ]. D o v re m m o sp o stare 5 all’in iz io d e ll’array, in a[o], d o v e n a tu ­
per selezione ordina un array
cercando ripetutamente l'elemento
ra lm e n te c ’è g ià u n e le m e n to , p re c isa m e n te il n u m e ro 1 1 . D i c o n s e g u e n z a n o n p o ssia m o
minimo della zona terminale s e m p lic e m e n te sp o stare a [ 3 ] in a[o] senza sp o stare 11 da q u a lc h e altra p a rte . N o n sap p iam o
■ non ancora ordinata, spostandolo a n c o ra d o v e d o v rà a n d a re a fin ire il n u m e ro 1 1 , m a sa p p ia m o c o n c e rte z z a c h e n o n d ev e
all'inizio della zona stessa. stare in a[o]. C e lo to g lia m o s e m p lic e m e n te di to r n o sc a m b ia n d o lo c o n a[3].

[0][1][2][3][4]

|'5l9 |l7|ll|Ì2

O ra il p r im o e le m e n to si trova n el p o s to g iu sto . N e lla fig u ra la z o n a p iù scu ra in d ic a la


p o r z io n e di array c h e è già stata o rd in a ta , m e n tr e la p a rte rim a n e n te è a n c o ra da o rd in a re .
S u c c e ssiv a m e n te c e rc h ia m o l’e le m e n to m in im o tra q u e lli r im a n e n ti, a [ 1 ] . . . a[4].
T ale v alo re m in im o , 9, si tro v a g ià n ella p o s iz io n e c o rre tta : in q u e s to caso n o n d o b b ia m o
fare n u lla e p o ssia m o s e m p lic e m e n te e s te n d e re di u n a p o s iz io n e v erso d estra la p o rz io n e
di array c h e risu lta essere g ià o rd in a ta .

[0][1][2][3][4]

11 12

R ip e tia m o il p r o c e d im e n to . Il v alo re m in im o della z o n a n o n o rd in a ta è l i , c h e d ev e


essere sc a m b ia to c o n il p r im o v alo re di tale z o n a , 17.

[0][1][2][3][4]

|j5Ì9gl7|ll|l2

O ra la z o n a n o n o rd in a ta è c o m p o s ta da d u e soli e le m e n ti, m a c o n tin u ia m o ad a p p lica re


la stessa stra te g ia v in c e n te . Il v alo re m in im o è 12 e lo sc a m b ia m o c o n il p r im o v alo re, 17.

[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;

Questo programma applica l'algoritmo di ordinamento


per selezione a un array riempito con numeri casuali.
*/
public class SelectionSortDemo
{
public static void main(String[] args)
{
int[] a = ArrayUtil.randomintArray(20, lOO);
System.out.println(Arrays.toString(a));

Select ionSorter.sort(a);

System.out.println(Arrays.toString(a));

File ArrayUtil.java
import java.util.Random;

Questa classe contiene metodi utili per elaborare array.


*/
public class ArrayUtil
{
private static Random generator = new 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;
}
}

Esempio di esecuzione del programma


[65, 46, 14, 52, 38, 2, 96, 39, 14, 33, 13, 4, 24, 99, 89, 77, 73, 87, 36, 8l]
[2, 4, 13, 14, 14, 24, 33, 36, 38, 39, 46, 52, 65, 73, 77, 81, 87, 89, 96, 99]

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

c h e restitu isc e il n u m e ro di m illise c o n d i c h e so n o trasc o rsi dalla m e z z a n o tte del g io r n o


1 g e n n a io 1970. O v v ia m e n te n o n ci in te ressa il n u m e ro asso lu to di s e c o n d i trasco rsi da
q u e ll’ista n te p a rtic o la re , p e rò la differenza fra d u e c o n te g g i di q u e s to g e n e re ci fo rn isc e
la d u ra ta di u n in te rv a llo te m p o ra le , m isu ra ta in m illise c o n d i.
E c c o il c o d ic e della classe Stopwatch:

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;

Costruisce un cronometro fermo, con il tempo


totale misurato che vale zero.
*/
public StopWatchO
{
reset 0 ;
}

Fa partire il cronometro, iniziando a misurare il tempo.


*/
public void start0
{
if (isRunning) { return; }
isRunning = true;
StartTime = System.currentTimeMillis();
}

/**
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

public long getEIapsedTimeO


{
if (isRunning)
{
long endTime = System.currentTimeMillis();
return = elapsedTime + endTime - startTime;
}
else
{
return elapsedTime;
}
}

/* *
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;

Questo programma misura il tempo richiesto per


ordinare, mediante l'algoritmo di ordinamento per
selezione, un array di dimensione specificata dall'utente.
*/
public class SelectionSortTimer
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter array size: ");
int n = in.nextI n t O ;

// costruisce un array casuale

int[] a = ArrayUtil.randomIntArray(n, lOO);

// usa il cronometro per misurare il tempo

StopWatch timer = new StopWatch();

timer.start();
SelectionSorter.sort(a);
timer. stopO;
686 C a p it o l o 13

System.out.println("Elapsed time: "


+ timer.getElapsedTimeO + " milliseconds");

Esempio di esecuzione del programma


Enter array size: 50000
Elapsed time: 13321 milliseconds

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)

5. Q u a n ti s e c o n d i sa reb b ero n e c e s sa r i, a p p r o s sim a tiv a m e n te , p e r o rd in a re u n in s ie m e di dati


c o n t e n e n t e 8 0 0 0 0 va lo ri?
6. O sse r v a te il g r a fic o d ella F igu ra 1: a q u a le c u r v a m a te m a tic a a sso m ig lia ?
O r d in a m e n t o e r ic e r c a 687

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 .

JtoalKÙlgItestazionidell'ordinam entafia selezione^


P ro v ia m o a c o n ta re le o p e ra z io n i c h e il p ro g ra m m a d ev e e s e g u ire p e r o rd in a re u n
a rray u sa n d o l’a lg o ritm o d i o rd in a m e n to p e r se lez io n e. N o n sa p p iam o , in realtà, q u a n te
o p e ra z io n i m a c c h in a v e n g o n o g e n e ra te p e r ciasc u n a is tru z io n e Java e n e p p u re q u ali di
q u e s te istru z io n i im p ie g a n o p iù te m p o di altre, p e rò p o ssia m o fare u n a se m p lific a z io n e : ci
lim ite re m o a c o n ta re il n u m e ro di visite di e le m e n ti d e ll’array, s a p e n d o c h e ciasc u n a visita
r ic h ie d e all’in c irc a la stessa q u a n tità d i la v o ro d o v u ta a n c h e ad altre o p e ra z io n i c o rre la te ,
q u ali l’in c re m e n to di in d ic i e il c o n f r o n to di v alo ri.
I n d ic h ia m o c o n n la d im e n s io n e d e ll’array. P er p r im a co sa d o b b ia m o tro v a re il
m in im o fra n n u m e ri: c iò r ic h ie d e la visita d eg li n e le m e n ti d e ll’array. P oi sc a m b ia m o gli
e le m e n ti, o p e ra z io n e c h e r ic h ie d e d u e v isite (p o tre ste a rg o m e n ta re c h e esiste u n a c e rta
p ro b a b ilità c h e n o n si d e b b a n o sc am b iare i v alo ri: è vero, e si p o tr e b b e m ig lio ra re l’analisi
p e r te n e re c o n to di q u e sta o sse rv a z io n e , m a , c o m e v e d re m o fra u n m o m e n to , se a n c h e lo
fac essim o n o n a lte re re m m o la c o n c lu s io n e co m p lessiv a). N e l passo successivo d o b b ia m o
v isitare so lta n to n —\ e le m e n ti p e r tro v are il m in im o ; n el passo a n c o ra s e g u e n te v e n g o n o
v isitati so lta n to n — 2 e le m e n ti; l’u ltim o passo visita s o lta n to d u e e le m e n ti, tra i q u ali
d ev e n u o v a m e n te tro v are il m in im o . C ia sc u n passo, p o i, r ic h ie d e 2 v isite p e r sc am b iare
gli e le m e n ti. Q u in d i, il n u m e ro to ta le d elle v isite è:

« + 2 + (« - 1) + 2 + •••2 + 2 = (« + (ff - 1) -f •••+ 2) + (h - 1) •2


= (2 + --- + ( « - l ) + «) + ( « - l ) * 2

p e rc h é

n{n + 1 )

S v ilu p p a n d o le m o ltip lic a z io n i e ra c c o g lie n d o n a fa tto re c o m u n e , tro v ia m o c h e il n u m e ro


d elle v isite è:

—;r + —» - 3
2 2

A b b ia m o o tte n u to u n ’e q u a z io n e q u a d ra tic a in n: q u e s to sp ieg a p e rc h é il g ra fic o della


F ig u ra 1 asso m ig lia a u n a p ara b o la.
O ra s e m p lific h ia m o u lte r io r m e n te l’analisi. Q u a n d o u sia m o u n v alo re di n elev ato ,
c o m e 1 0 0 0 o 2 0 0 0 , il te r m in e {l/2)n^ è u g u a le a 5 0 0 0 0 0 o, ris p e ttiv a m e n te , 2 0 0 0 0 0 0 .1
te rm in i di g ra d o in fe rio re , (5 /2 )« - 3, n o n c o n trib u is c o n o p iù di ta n to , v a lg o n o a p p e n a
2 4 9 7 o, risp e ttiv a m e n te , 4 9 9 7 , u n a g o c c ia n el m a re ris p e tto alle c e n tin a ia di m ig liaia o
688 C a p it o l o 13

a d d irittu ra ai m ilio n i di visite c o r r is p o n d e n ti al te rm in e q u a d ra tic o . S e m p lic e m e n te ,


ig n o re re m o q u e sti te rm in i d i g ra d o in fe rio re , così c o m e il fa tto re c o s ta n te 1 /2 : n o n ci
in teressa il n u m e ro e ffettiv o d elle v isite p e r u n sin g o lo v alo re di n, v o g lia m o s o lta n to
c o n fro n ta re i ra p p o rti d ei v a lo ri p e r diversi v a lo ri di n. P e r e se m p io , p o ssia m o d ire c h e
o rd in a re u n array d i 2 0 0 0 n u m e r i r ic h ie d e u n n u m e ro di v isite p a ri a 4 v o lte q u e lle
n e c e ssa rie p e r o rd in a re u n arra y di 1 0 0 0 n u m e ri:

-■ 2 0 0 0 ^
2
= 4
1 2
-•1 0 0 0 ^
2

11 fa tto re 1 /2 si e lid e in c o n fro n ti di q u e s to g e n e r e :d ir e m o s e m p lic e m e n te c h e “ il n u m e ro


d elle v isite è d e ll’o rd in e di In q u e s to m o d o p o ssia m o a g e v o lm e n te o sserv are c h e il
n u m e ro di v isite q u a d ru p lic a q u a n d o le d im e n s io n i d e ll’array ra d d o p p ia n o , p e rc h é (2«)^
= 4«2.
P e r in d ic a re c h e il n u m e ro d elle v isite è d e ll’o rd in e di spesso n e ll’in fo rm a tic a
Neirinformatica teorica si descrive
te o ric a si usa la n o ta z io n e O -g ra n d e (hig-Oh notation). 11 n u m e ro d elle v isite è 0(n-):sì
il tasso di crescita di una funzione
usando la notazione 0-grande.
tra tta di u n a c o m o d a a b b re v ia z io n e , di c u i d a re m o u n a d e fin iz io n e fo rm a le n ella se z io n e
A rg o m e n ti av an zati 13.1.
P e r tra sfo rm a re u n ’esp re ssio n e esatta c o m e

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 .

Atgojmenti avanzati 13.1


O-grande, Omega (Q ) e Theta ( 6 )
In q u e s t o c a p i t o lo a b b ia m o u s a t o in m o d o u n p o ’ a p p r o s s im a t iv o 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 c o m p o r t a m e n t o d e lla c r e s c ita d i u n a f u n z i o n e : v e d i a m o c o m e si p u ò
d e s c r iv e r e p iù f o r m a l m e n t e la n o t a z i o n e O - g r a n d e . S ia d a ta u n a f u n z i o n e T (« ), c h e
s o l i t a m e n t e , n e l n o s t r o c a s o , r a p p r e s e n ta il t e m p o r ic h i e s t o d a u n a l g o r i t m o p e r e la b o r a r e
d a ti d ’in g r e s s o d i d i m e n s i o n e / / ,m a p o t r e b b e e s s e r e u n a f u n z i o n e q u a lsia si. S u p p o n i a m o ,
p o i , c h e e sista u n ’altra f u n z i o n e ,y ( /i ) : s o l i t a m e n t e v i e n e s c e lta u n a f u n z i o n e “ s e m p l i c e ” ,
ad e s e m p i o ^ / / ) = o p p u r e ^ / i ) = lo g ( /i ) , m a a n c h e q u e s ta p u ò e s s e r e u n a q u a lsia si
f u n z i o n e . S c r iv i a m o

T(M ) = 0 ( / ( m))

se la c r e s c ita d i T ( n ) è s u p e r i o r m e n t e lim it a t a d a lla c r e s c ita d ì j { r ì ) . D e t t o in m o d o p iù


f o r m a le , d e v e e s s e r e v e r o c h e p e r o g n i v a lo r e d i n m a g g i o r e d i u n a q u a lc h e s o g lia sia T { n )
/ J [ n ) ^ C , c o n C v a lo r e c o s t a n t e .

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

D ic e n d o c h e T{n) = 0(J{n)) si affe rm a c h e T n o n cresce p iù v e lo c e m e n te di / , m a è


p o ssib ile c h e T cresca m o lto p iù le n ta m e n te , p e r c u i è te c n ic a m e n te c o r r e tto d ire c h e
T{n) = / r + 5ti - 3 è 0{rr) o a n c h e
G li in fo rm a tic i te o ric i h a n n o in v e n ta to u lte r io ri n o ta z io n i c h e d e s c riv o n o in m o d o
p iù a c c u ra to il m o d o in c u i c re sc o n o le fu n z io n i. L’esp re ssio n e

T(«) = Q (/(« ))

sig n ifica c h e T cresce a lm e n o ta n to v e lo c e m e n te q u a n to c r e s c e / o p p u re , in m o d o p iù


fo rm a le , c h e p e r tu tti i v a lo ri di n m a g g io ri d i u n a c e rta soglia si h a T{n)/J{n) > C , c o n
C c o s ta n te (il sim b o lo Q è la le tte ra o m e g a m a iu sc o la d e ll’alfa b e to g re c o ). A d e sem p io ,
T{n) = «“ H- 5« - 3 è o a n c h e Q.{n).
L’esp re ssio n e

T(«) = © (/(«))

sig n ifica c h e T e f c re sc o n o c o n la stessa v e lo c ità , c io è è v ero sia c h e T{n) = 0{f[n)) sia


c h e T{n) = ^{f{n)) (il sim b o lo 0 è la le tte ra th e ta m a iu sc o la d e ll’a lfa b e to g re c o ).
La n o ta z io n e 0 fo rn is c e la p iù precisa d e s c riz io n e d e ll’a n d a m e n to di cre scita di u n a
fu n z io n e . A d e se m p io , T{n) = 5n - 3 è Q{n^) m a n o n è Q{n) n é Q{n^).
Q u e s te n o ta z io n i so n o m o lto im p o r ta n ti p e r e ffe ttu a re u n ’analisi d eg li a lg o ritm i
c o n u n a c e rta p re c isio n e , m a è p ra tic a c o m u n e p arla re s e m p lic e m e n te di O -g r a n d e , p u r
f o rn e n d o p e r tale n o ta z io n e la stim a p iù p recisa p o ssib ile.

A«iim)enlLayanzfllii3.2 ___________ ______________ ____


Ordinamento per inserimento
L’o rd in a m e n to p e r in se rim e n to {insertion sort) è u n altro sem p lic e a lg o ritm o di o rd in a m e n to ,
n el q u a le si s u p p o n e c h e la p a rte in iziale

a [0 ] a [ l ] . . . a[k]

di u n a rray sia g ià o rd in a ta (q u a n d o l’a lg o ritm o in izia il su o lavoro, k vale 0). E sp a n d ia m o


q u e sta p a rte in iz ia le o rd in a ta in se re n d o v i n ella g iu sta p o s iz io n e il successivo e le m e n to
d e ll’array, a[k + i ] . G iu n ti al te rm in e d e ll’array, il p ro c e sso di o rd in a m e n to è c o m p le ta to .
O r d in a m e n t o e r ic e r c a 691

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

La p a rte in iziale, di lu n g h e z z a 1, è o v v ia m e n te g ià o rd in a ta . A g g iu n g ia m o o ra a tale


p o rz io n e o rd in a ta l’e le m e n to a [ i ] , il cu i v alo re è 9 . T ale e le m e n to d ev e essere in s e rito
p rim a d e ll’e le m e n to di v alo re 11, p e r cu i il risu lta to è:

P ro c e d ia m o a g g iu n g e n d o l’e le m e n to a [2], il c u i v alo re è 16: n o n c ’è b iso g n o di spostarlo.

R ip e tia m o il p r o c e d im e n to in s e re n d o l’e le m e n to a [ 3 ] ,d i v alo re 5, nella p rim a p o siz io n e


della p o r z io n e iniziale.

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 .

public class InsertionSorter


{

Ordina un array con l'algoritmo di ordinamento per inserimento.


@param a !'array da ordinare
*/
public static void sort(int[] a)
{
for (int i = 1; i < a.length; i++)
{
int next = a[i];
// sposta in avanti tutti gli elementi maggiori
int j = i;
while (j > 0 && a[j - l] > next)
{
a[j] = a[j - 1 ];
j--;
}
// inserisci l'elemento
a[j] = next;
}
}
}

Q u a n to è effic ie n te q u e s to a lg o ritm o ? In d ic h ia m o c o n n la d im e n s io n e d e ll’array, p e r il


c u i o r d in a m e n to e s e g u ia m o n —1 ite ra z io n i. D u ra n te la /?-esim a ite ra z io n e a b b ia m o u n a
p o r z io n e già o rd in a ta di k e le m e n ti, nella q u a le d o b b ia m o in se rire u n n u o v o e le m e n to ;
ciascu n in s e rim e n to ric h ie d e di visitare gli e le m e n ti della p o rz io n e in iziale o rd in a ta fin c h é
692 C a p it o l o 13

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.

13.4 Ordinamento perfusione (MeraeSort)


In q u e s t o p a r a g r a fo s t u d ie r e t e l ’a l g o r i t m o d i o r d i n a m e n t o p e r f u s io n e , c h e è m o l t o p iù
e f f i c ie n t e d e H ’o r d i n a m e n t o p e r s e l e z i o n e , a n c h e se l ’id e a su c u i si b a sa è m o l t o s e m p l i c e .
S u p p o n i a m o d i a v e r e u n array d i 1 0 n u m e r i i n t e r i. P r o v i a m o p e r u n a v o lt a a e sse r e
o t t i m is t i e s p e r i a m o c h e la p r im a m e t à d e l l ’a rray sia g ià p e r f e t t a m e n t e o r d in a ta e c h e lo
sia a n c h e la s e c o n d a m e t à , c o m e in q u e s t o c a so :

HZ 10 12 17 11 20 32

A q u e s t o p u n t o è fa c ile f o n d e r e i d u e array o r d in a ti in u n s o l o array o r d in a t o , s e m p l i c e m e n t e


p r e le v a n d o u n n u o v o e l e m e n t o d al p r i m o o d a l s e c o n d o s o t t o - a r r a y , s c e g l i e n d o o g n i
v o lt a l ’e l e m e n t o p iù p i c c o l o :

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

T u tto q u e s to sarà a n c h e d iv e rte n te , m a n o n se m b ra c h e possa essere u n a so lu z io n e


di ordinamento perfusione
ordina un array dividendolo a metà, d el p ro b le m a p e r il c o m p u te r, c h e si tro v a a d o v e r a n c o ra o rd in a re la p rim a e la se c o n d a
ordinando ricorsivamente ciascuna m e tà d elF array : n o n p u ò c e r to c h ie d e re a q u a lc h e a m ic o di d arg li u n a m a n o . S c o p ria m o ,
delle due parti e fondendo, poi, p e rò , c h e , se il c o m p u te r c o n tin u a a s u d d iv id e re T array in a rray se m p re p iù p ic c o li,
le due metà ordinate. o r d in a n d o cia sc u n a m e tà e fo n d e n d o le p o i in sie m e , i passi c h e d ev e ese g u ire s o n o assai
m e n o n u m e ro si di q u elli ric h ie sti d a lT o rd in a m e n to p e r se le z io n e .
P ro v ia m o a sc riv e re u n a classe, MergeSorter, c h e im p le m e n ti q u e sta id ea. Q u a n d o il
su o m e to d o sort o rd in a u n array, crea d u e array, c ia sc u n o av e n te d im e n s io n e p a ri alla
m e tà d elT array o rig in a rio , e li o rd in a ric o rsiv a m e n te . Q u in d i, fo n d e in sie m e i d u e array
o rd in a ti:

public static void sort(int[] a)


{
if (a.length <= l) { return; }
int[] first = new int[a.length / 2];
int[] second = new int [a. length - first, length];
// copia in first la prima metà e in second la seconda

sort (first);
sort(second);
merge(first, second, a);
}

Il m e to d o merge è n o io s o m a ab b a sta n za se m p lic e: lo tro v e re te n el c o d ic e c h e segue.

File MergeSorter.java

Il metodo sort di questa classe ordina un array


usando l'algoritmo di ordinamento per fusione.
*/
public class MergeSorter
{

Ordina un array usando l'algoritmo di ordinamento per fusione.


@param a !'array da ordinare
*/
public static void sort(int[] a)
{
if (a.length <= l) { return; }
int[] first = new int [a. length / 2];
int[] second = new int [a. length - first, length];
// copia in first la prima metà e in second la seconda
for (int i = 0; i < first.length; i++)
{
first[i] = a[i];
}
for (int i = 0; i < second.length; i++)
{
second [i] = a [first, length + i];
}
sort (first);
sort(second);
694 C a p it o l o 13

27 merge(first, second, a);


28
29
30 /**
31 Fonde in un array due array ordinati.
32 @param first il primo array ordinato
33 @param second il secondo array ordinato
34 @param a !'array in cui viene memorizzato il risultato della fusione
35 */
36 private static void merge(int[] first, int[] second, int[] a)
37 {
38 int iFirst = 0; // il prossimo elemento da considerare nel primo array
39 int iSecond = 0; // il prossimo elemento da considerare nel secondo array
40 int j = 0; // la prossima posizione libera nell'array a
41
42 // finché né iFirst né iSecond oltrepassano la fine
43 // del relativo array, sposta in a l'elemento minore
44 while (iFirst < first.length 8i& iSecond < second.length)
45 {
46 if (first [iFirst] < second [iSecond])
47 {
48 3[j] = first [iFirst];
49 iFirst++;
50 }
51 else
52 {
53 a[j] = second[!Second];
54 !Second++;
55 }
56 j++;
57 }
58
59 // notate che soltanto uno dei due cicli seguenti viene eseguito:
60 // copia in a tutti i valori rimasti nel primo array
61 while (iFirst < first.length)
62 {
63 3[j] = first[ iFirst];
64 iFirst++; j++;
65 }
66 // copia in a tutti i valori rimasti nel secondo array
67 while (iSecond < second.length)
68 {
69 a[j] = second[iSecond];
70 !Second++; j++;
71 }
72
73 }

File MergeSortDemo.java
import java.util.Arrays;

Questo programma applica l'algoritmo di ordinamento


per fusione a un array riempito con numeri casuali.
O r d in a m e n t o e r ic e r c a 695

*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));
}
}

Esempio di esecuzione del programma


[8, 81, 48, S3, 46, 70, 98, 42, 27, 76, 33, 24, 2, 76, 62, 89, 90, 5, 13, 21]
[2, 5, 8, 13, 21, 24, 27, 33, 42, 46, 48, 53, 62, 70, 76, 76, 81, 89, 90, 98]

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 .

L’a lg o ritm o di o r d in a m e n to p e r fu s io n e se m b ra m o lto p iù c o m p le s s o deH ’a lg o ritm o


d i o r d in a m e n to p e r s e le z io n e e ci si im m a g in a c h e possa im p ie g a re m o lto p iù te m p o
p e r e s e g u ire tu tte q u e s te r ip e t u te s u d d iv is io n i. In v e c e , i te m p i c h e si rile v a n o c o n
l’o r d in a m e n to p e r fu s io n e so n o d e c is a m e n te m ig lio r i d i q u e lli re la tiv i a ll’o r d in a m e n to
p e r s e le z io n e .
La F ig u ra 2 m o stra u n a ta b ella e u n g ra fic o c h e m e tto n o a c o n f r o n to i d u e in sie m i
di m is u ra z io n i d e lle p re sta z io n i: c o m e si p u ò v ed e re, c o n l’o r d in a m e n to p e r fu sio n e il
m ig lio ra m e n to è stre p ito so . P er c a p irn e il m o tiv o , p ro v ia m o a stim a re il n u m e ro di v isite
agli e le m e n ti d elP a rray c h e s o n o n e c e ssa rie p e r o rd in a re u n array m e d ia n te l’a lg o ritm o di
o r d in a m e n to p e r fu sio n e. A ffro n tia m o p e r p rim a cosa il p ro ce sso di fu sio n e c h e si e ffettu a
d o p o c h e la p rim a e la s e c o n d a m e tà so n o state o rd in a te .
696 C a p it o l o 13

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 :

T{n) = T(n/2) + T{n/2) -f 5n

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

T{n) = 2T{n/2) + Sri

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 :

T{n/2) = 2T{ri/4) + 5n/2

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

G e n e ra liz z a n d o da 2, 4, 8 a p o te n z e a rb itra rie di 2:

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,

T ( n ) = 2 '" T ( aa/2 " ’) + S n m


= n T {\) + Snm

= n S n l o g 2«

p e rc h é n = 2"' im p lic a m = lo g 2 («).


P er c a p ire c o m e cre sce la f u n z io n e e lim in ia m o il te r m in e di g ra d o in fe rio re , n,
r im a n e n d o c o n S n lo g 2 (/i). E lim in ia m o a n c h e il fa tto re c o sta n te , 5. D i so lito si trasc u ra
a n c h e la base d el lo g a ritm o , p e rc h é tu tti i lo g a ritm i so n o tra lo ro c o rre la ti tra m ite u n
fa tto re c o sta n te . P e r e se m p io :

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

S u p p o n ia m o p e r u n m o m e n to c h e , p e r o rd in a re u n array d i 1 0 0 0 0 n u m e ri, l’o r d in a m e n to


p e r fu sio n e im p ie g h i lo stesso te m p o c h e im p ie g a l’o r d in a m e n to p e r se le z io n e , c io è tre
q u a rti d i s e c o n d o sulla n o stra m a c c h in a di pro v a (in realtà, è m o lto p iù v elo ce ). A llo ra p e r
o rd in a re u n m ilio n e di n u m e ri in te ri im p ie g h e re b b e circa 0 .7 5 x 150 se c o n d i, o v v ero
m e n o d i 2 m in u ti: c o n fro n ta te q u e s to te m p o c o n q u e llo ric h ie s to d a ll’o rd in a m e n to p e r
se le z io n e , c h e ci m e tte re b b e p iù di 2 o re p e r e se g u ire lo stesso c o m p ito . C o m e p o te te
v ed e re, a n c h e se vi s e rv o n o a lc u n e o re p e r im p a ra re u n a lg o ritm o m ig lio re , si tra tta di
te m p o b e n speso.
698 C a p it o l o 13

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 .

Argomenti avanzati 13.3____ _________________ ____ ______


L'algoritmo Quicksort
Q u i c k s o r t è u n a lg o r i t m o d i fr e q u e n t e u t iliz z o , c h e h a il v a n t a g g io , r is p e t t o a ll’o r d i n a m e n t o
p e r f u s io n e , d i n o n a v e r b i s o g n o d i array t e m p o r a n e i p e r o r d in a r e e f o n d e r e i r is u lta ti
p a r z ia li.
L’a l g o r i t m o q u i c k s o r t , c o m e l ’o r d i n a m e n t o p e r f u s io n e , si b a sa su lla s tr a te g ia d i
“ d i v id e r e p e r v i n c e r e ” { d i v i d e a n d c o n q u e r ) . P e r o r d in a r e la p o r z i o n e a [from] . . . a[to]
d e l l ’a rray a, si d i s p o n g o n o d a p p r im a g li e l e m e n t i in m o d o c h e n e s s u n o d i q u e lli p r e s e n t i
n e lla p a r te a [from] . . . a [p] sia m a g g i o r e d i e l e m e n t i d e l l ’a ltra p a r te , a [p + l ] . . . a[to].
Q u e s t o p a s so v i e n e d e t t o s u d d iv i s i o n e o p a r t i z i o n a m e n t o d e lla p o r z io n e .
A d e s e m p i o , s u p p o n e t e d i in iz ia r e c o n q u e s ta p o r z i o n e

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

E c c o u n ’im p le m e n ta z io n e ric o rsiv a di q u ic k so rt:

public static void sort(int[] a, int from, int to)


{
if (from >= to) { return; }
int P = partition(a, from, to);
sort(a, from, p);
sort(a, P + I, to);
}

T o rn ia m o al p ro b le m a d i c o m e s u d d iv id e re in d u e p a rti u n a p o r z io n e di array. S c e g lie te


u n e le m e n to e c h ia m a te lo pivot (c a rd in e ). E s is to n o d iv e rse v a ria n ti d e ll’a lg o r itm o
q u ic k s o rt: n ella p iù se m p lic e , s c e g lie re te c o m e p iv o t il p r im o e le m e n to d ella p o r z io n e ,
c io è a[from ].
O r a c re a te d u e re g io n i: a [from] . . . a[i], c o n t e n e n te v a lo ri n o n m a g g io r i d el
p iv o t, e a[ j ] . . . a [to], c o n te n e n te v a lo ri n o n m in o r i d el p iv o t. La re g io n e a[i+i] . . .
a[j - 1] c o n tie n e v alo ri c h e n o n so n o a n c o ra stati an alizzati. A ll’in iz io le z o n e di sinistra
e di d estra so n o v u o te , c io è i = from - i e j = to + i.

Suddivisione Non ancora


< pivot elaborata > pivot
di una porzione di array
da ordinare

[from] [i] [j] [to]

A q u e s to p u n to , in c re m e n ta te i fin c h é a[i] < pivot e d e c re m e n ta te j fin c h é a[j] > pivot.


La fig u ra m o stra i e j q u a n d o il p ro c e d im e n to si arresta.

Estensione delle due parti, > pivot < pivot


sinistra e destra

< pivot < pivot I > pivot > pivot

[from] [i] [j] [to]

O r a sc a m b ia te tra lo ro i v alo ri c h e si tro v a n o n e lle p o siz io n i i e j, e s te n d e n d o così


e n tra m b e le z o n e , p o i p ro s e g u ite c o n la p ro c e d u ra p r e c e d e n te fin c h é i < j. E c c o il c o d ic e
p e r il m e to d o partition:

private static int partition(int[] a, int from, int to)


{
int pivot = a[from];
int i = from - 1;
int j = to + 1;
while (i < j)
{
700 C a p it o l o 13

i++; while (a[i] < pivot) { i++;}


j - ; while (a[j] > pivot) { j - ; }
if (i < j) { ArrayUtil.swap(i, j); }
}
return j;
}

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 .

13.6.1 Ricerca lineare


I m m a g in a t e d i v o l e r tr o v a re il n u m e r o d i t e l e f o n o d i u n v o s t r o a m ic o . C e r c a t e il s u o n o m e
n e l l ’e l e n c o t e l e f o n i c o e , o v v i a m e n t e , l o tr o v a te r a p id a m e n t e , p e r c h é l ’e l e n c o t e l e f o n i c o è
o r d in a t o a lf a b e t ic a m e n t e . P e n s a te , o r a , d i a v e r e u n n u m e r o d i t e l e f o n o e d i v o l e r sa p e r e
a c h i a p p a r t ie n e : p o t r e s t e , n a t u r a lm e n t e , c h ia m a r e q u e l n u m e r o , m a s u p p o n i a m o c h e
n e s s u n o r is p o n d a alla c h ia m a ta ; in a lte r n a tiv a , p o t r e s t e s c o r r e r e l ’e l e n c o t e l e f o n i c o , u n
n u m e r o d o p o l ’a ltro , f i n o a q u a n d o tr o v a te q u e l l o c h e v i in te r e ss a . Q u e s t o c o m p o r t e r e b b e ,
o v v i a m e n t e , u n ’e n o r m e q u a n tità d i la v o ro : d o v r e s t e e sse r e d a v v e r o d isp e r a ti p e r im b a r c a r v i
in u n ’im p r e s a d e l g e n e r e .
Q u e s t o i p o t e t i c o e s p e r i m e n t o fa c a p ir e la d if f e r e n z a fra la r ic e r c a e f fe t t u a t a in u n
i n s i e m e d i d a ti o r d in a t i e q u e lla c h e o p e r a c o n d a ti n o n o r d in a ti: i p r o s s im i d u e p a r a g r a fi
e s a m i n e r a n n o q u e s ta d if f e r e n z a in m o d o p iù f o r m a le .
S e v o l e t e tr o v a re u n n u m e r o a ll’i n t e r n o d i u n a s e q u e n z a d i v a lo r i c h e si p r e s e n t a n o in
Laricerca lineare esamina tutti i valori
di un array fino a trovare u n o r d i n e a r b itr a r io , n o n p o t e t e fare n u lla p e r a c c e le r a r e la r ic e r c a : d o v e t e s e m p l i c e m e n t e

una corrispondenza con quanto s c o r r e r e tu tti g li e le m e n t i, e s a m in a n d o li u n o a u n o , fin o a quando tr o v a te u n a


cercato ola fine deH'array. c o r r is p o n d e n z a c o n l ’e l e m e n t o c e r c a t o o p p u r e a r r iv a te in f o n d o a ll’e l e n c o . Q u e s t a si
c h ia m a ricerca lineare {ìitiear search) o ricerca seq u en ziale {s e q u e n tia l search).
O r d in a m e n t o e r ic e r c a 701

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;

Questo programma utilizza l'algoritmo di ricerca lineare.


*/
public class LinearSearchDemo
{
public static void main(String[] args)
{
int[] a = ArrayUtil.randomIntArray(20, 100);
System.out.printIn(Arrays.toString(a));
Scanner in = new Scanner(System.in);

boolean done = false;


while (!done)
{
System.out.print("Enter number to search for, -I to quit: ");
702 C a p it o l o 13

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 );
}

Esempio di esecuzione del programma

[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

13.6.2 Ricerca binaria


C -c R 'liia m o o ra u n e le m e n t o all’in t e r n o d i u n a s e q u e n z a d i d ati c h e sia stata p r e c e d e n t e m e n t e
o r d in a ta . N a t u r a l m e n t e p o t r e m m o a n c o r a e s e g u ir e u n a r ic e r c a lin e a r e , m a p o s s i a m o far
d i m e g l i o , c o m e si v e d r à .
C o n s i d e r a n d o q u e s t o array o r d in a t o , a:

[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 .

ripetendo poi la ricerca in una L a c la s se s e g u e n t e r e a liz z a la r ic e r c a b in a r ia a ll’in t e r n o d i u n array o r d in a t o d i n u m e r i


delle due metà. i n t e r i . Il m e t o d o search r e s tit u is c e la p o s i z i o n e d e H ’e l e m e n t o c e r c a t o se la r ic e r c a h a
s u c c e s s o , o p p u r e —1 se il v a lo r e c e r c a t o n o n è p r e s e n t e in a. L’a l g o r i t m o d i r ic e r c a b in a r ia
v i e n e q u i m o s t r a t o n e lla su a v e r s io n e r ic o r s iv a .

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;
}

P ro v ia m o o ra a stab ilire q u a n te v isite di e le m e n ti d e lP a rra y so n o n e c e ssa rie p e r p o rta re


a te rm in e u n a ric e rc a b in a ria . P o ssia m o usare la stessa te c n ic a c h e a b b ia m o a d o tta to p e r
an a lizzare l’o r d in a m e n to p e r fu sio n e e o sserv are c h e , p o ic h é e s a m in ia m o l’e le m e n to
c e n tra le , c h e c o n ta c o m e u n a sola visita, e p o i e s p lo ria m o il s o tto -a rra y di sin istra o p p u re
q u e llo di d estra , p o ssia m o scriv ere:

T(n) = T{n/2) + 1

U tiliz z a n d o la stessa e q u a z io n e , si ha:

T(n/2) = T(n/4) + 1

In se re n d o q u e s to risu lta to n e ll’e q u a z io n e o rig in a le , o tte n ia m o :

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

C o m e n e ll’analisi d e ll’o r d in a m e n to p e r fu sio n e , fa c c ia m o l’ip o te si sem p lific ativ a c h e n


sia u n a p o te n z a di 2, c io è n = 2'^, d o v e m è log^(«). O tte n ia m o , q u in d i

T{n) = 1 + log2(«)

D i c o n s e g u e n z a , la ric e rc a b in a ria è u n a lg o ritm o 0 (lo g (« )).


Q u e s to r is u lta to h a se n so a n c h e dal p u n to di v ista in tu itiv o . S u p p o n ia m o c h e n
La ricerca binaria trova la posizione
valga 100: d o p o c ia sc u n a ric e rc a la d im e n s io n e d e ll’in te rv a llo da e s p lo ra re v ie n e divisa
di un valore in un array eseguendo
un numero di passi 0(log(n)).
a m e tà , rid u c e n d o s i v ia v ia a: 5 0 , 2 5 , 12, 6, 3 e 1. D o p o s e tte c o n f r o n ti a b b ia m o fin ito :
q u e s to c o in c id e c o n la n o s tra f o rm u la , dal m o m e n to c h e Iog^(IOO) ~ 6 .6 4 3 8 6 e, in
e ffe tti, la p iù p ic c o la p o te n z a d i 2 c o n e s p o n e n te in te r o il c u i v a lo re sia m a g g io re di
1 00 è 2^ = 128.
D al m o m e n to c h e la ric e rc a b in a ria è ta n to p iù v e lo c e della ric e rc a lin e are, vale la
p e n a di o rd in a re u n array n o n o rd in a to , p e r p o i ric o r r e r e alla ric e rc a b in a ria ? D ip e n d e .
Se n e ll’array si e ffe ttu a u n a sola ric e rc a , allo ra è p iù e ffic ie n te so ste n e re so lo il c o sto 0{n)
di u n a ric e rc a lin e a re in v e ce d el c o s to 0{n Iog(M)) di u n o r d in a m e n to , se g u ito da q u e llo
0(log(M )) di u n a ric e rc a b in a ria . Se, p e rò , su llo stesso arra y si d e v o n o e se g u ire m o lte
ric e rc h e , allo ra vale d av v e ro la p e n a di ese g u ire p rim a l’o rd in a m e n to .
O r d in a m e n t o e r ic e r c a 705

Computer e società 13.1


A A A

Il primo programmatore p e r gestire il rip o rto da u n a cifra alla


P r im a c h e e s is te s s e ro le c a lc o la ­ successiva. Al c o n tra rio , le m a c c h in e
tric i tascabili e i p e rs o n a l c o m p u ­ m o ltip lic atric i m e cc an ic h e era n o fra­
ter, n a v ig a to ri e in g e g n e ri usav an o 19 gili e p o c o affidabili. B abbage co stru ì
a d d i z io n a tr ic i m e c c a n ic h e , re g o li 27 u n p ro to tip o del D ifferen ce E n g in e
37 c h e e b b e successo e, c o n d e n a ro suo
c a lc o la to ri e ta v o le di lo g a ritm i e
64 e alcu n i fo n d i m essi a d isp o siz io n e
di fu n z io n i tr ig o n o m e tr ic h e p e r 61
ac ce lerare i calco li. S fo r tu n a ta m e n ­ dal g o v ern o , passò a p ro d u rre la m a c­
125
te, le tavole, i cu i v a lo ri d o v e v a n o 91 ch in a p e r stam pare le tavole. T uttavia,
essere calco lati a m a n o , e ra n o n o t o ­ 216 p e r p ro b lem i di fin an z iam en to e p e r
r ia m e n te im p re c ise . Il m a te m a tic o le d ifficoltà c h e si in c o n tra ro n o p e r
R ip e te te il p ro ce sso , s c riv e n d o nella co stru ire la m a cc h in a c o n la precisio­
C h a rles B a b b ag e (1 7 9 1 -1 8 7 1 ) e b b e
te rz a c o lo n n a le d iffe re n z e fra v a lo ri n e m ecc an ic a ch e era necessaria, n o n
l’in tu iz io n e ch e, q u a lo ra fosse stato
c o n s e c u tiv i della se c o n d a c o lo n n a , e v en n e m ai p o rta ta a te rm in e .
p o s s ib ile c o s tr u i r e u n a m a c c h in a
p o i rip e te te lo u n ’altra volta: M e n tre stava la v o ra n d o al D iffe­
c h e p ro d u c e sse a u to m a tic a m e n te le
tavole sta m p a te, si sa re b b e ro e v ita ­ re n c e E n g in e , B ab b ag e c o n c e p ì u n ’i­
ti sia gli e r r o ri di ca lc o lo sia q u e lli d ea m o lto p iù g ran d io sa, c h e c h ia m ò
di c o m p o s iz io n e tip o g ra fic a . B a b ­ 8 12
Analytical Engine. Il D iffe re n ce E n g in e
b a g e si d e d ic ò al p r o g e tto di u n a 19 6 era stato c o n c e p ito p e r eseguire u n in ­
tale m a c c h in a , c h e c h ia m ò Difference 27 18 sie m e lim ita to di calcoli e n o n era p iù
37 6 av an zato di u n a c a lc o latric e tascabile
Etiffne, p e rc h é u tilizzav a d iffe re n z e
64 24 d ei n o stri g io rn i, m a B ab b ag e si rese
c o n s e c u tiv e p e r ca lc o la re fu n z io n i 61 6
p o lin o m iali. P er esem p io , c o n sid e ra te c o n to c h e u n a m a c c h in a del g e n e re
125 30
la fu n zio n e /(!x j = S c riv e te i v alo ri 91 av re b b e p o tu to essere resa program­
p e r /( 1 ) ,^ 2 ) ,^ 3 ) e così di se g u ito , p o i 216 mabile, im m a g a z z in a n d o p ro g ra m m i
scriv ete, p iù a destra, le differenze fra in sie m e ai dati: la m e m o ria in te rn a
O ra le d iffe re n z e o tte n u te s o n o c o ­ d e ir A n aly tical E n g in e doveva essere
v alo ri co n s e c u tiv i:
sta n ti. P o te te ritro v a re i v a lo ri della co stitu ita da 1000 reg istri, c iasc u n o
Difference Engine di Babbage f u n z io n e m e d ia n t e u n a s e q u e n z a c o n 50 cifre d ecim ali; p ro g ra m m i e
d i a d d iz io n i: d o v e te c o n o s c e r e la co stan ti d o v ev a n o essere m e m o riz z a ti
d iffe ren za c o s ta n te e i v a lo ri c h e si su sc h e d e p erfo ra te, u n a te cn ic a c h e
tro v a n o sul b o r d o s u p e r io r e d e llo all’e p o c a era m o lto diffusa n ei telai
sch em a. P o te te provare: scriv ete su u n p e r tessere stoffe d e c o rate .
fo g lio di ca rta i n u m e ri e v id e n z ia ti, A d a A u g u s ta , c o n te ssa d i L o ­
r ie m p ie n d o le p o s iz io n i r im a n e n ti v e la c e ( 1 8 1 5 -1 8 5 2 ), u n ic a figlia di
c o n il risu lta to d e ll’a d d iz io n e d ei n u ­ L o rd B y ro n , fu a m ic a e fin a n z ia tric e
m e ri c h e si tro v a n o so p ra e in alto a d i C h a rle s B a b b a g e e fu u n a d e lle
d estra risp e tto al n u m e ro c h e si cerca. p r im e p e r s o n e a c a p ire il p o te n z ia le
Q u e sto m e to d o era m o lto attra­ di u n a m a c c h in a d e l g e n e r e , n o n
ente, p erch é le m a cc h in e addizionatrici s o lta n to p e r c a lc o la re ta v o le m a te ­
m e c c a n ic h e g ià si c o n o s c e v a n o da m a tic h e , m a p e r e la b o ra re d a ti c h e
te m p o : era n o co stitu ite da ru o te d e n ­ n o n fo ssero n u m e ri: d a m o lti v ie n e
tate, c o n dieci d en ti p e r ru o ta a rap p re­ c o n s id e ra ta il p r im o p ro g ra m m a to re
sentare le cifre e o p p o rtu n i m eccanism i del m o n d o .
706 C a p it o l o 13

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 .

13.7 Problem Solving: stima del tempo di esecuzione


di un algoritmo _ ,
In q u e s t o c a p i t o lo a v e t e v is t o c o m e si p o s sa s tim a r e il t e m p o d i e s e c u z i o n e d e g l i a l g o r it m i
d i o r d i n a m e n t o . I n o lt r e , c o m e a v r e te c e r t a m e n t e n o t a t o , sa p e r d is t i n g u e r e u n a l g o r i t m o
a v e n te p r e s ta z io n i 0 { n l o g ( « ) ) d a u n o 0 { r r ) h a g r a n d i im p l ic a z io n i p r a tic h e . A n a lo g a m e n t e ,
è m o l t o i m p o r t a n t e s a p e r s tim a r e le p r e s t a z io n i t e m p o r a li d i a ltri a l g o r i t m i e in q u e s t o
p a r a g r a fo c i e s e r c it e r e m o s t i m a n d o le p r e s t a z io n i d i a lc u n i a l g o r i t m i c h e e la b o r a n o array.

13.7.1 Algoritmi lineari


I n iz ia m o c o n u n e s e m p i o s e m p l i c e , u n a l g o r i t m o c h e c o n t a il n u m e r o d i e l e m e n t i c h e
h a n n o u n d e t e r m i n a t o v a lo r e :

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

' l / '1 '^ ' l . ' ^

®
' I ^

boolean found = false;


for (int i = 0; !found && i < a.length; i++)
{
if (a[i] == value) { found = true; }
}

In q u e s t o c a s o il c i c l o p u ò arresta rsi p r im a d i a v e r e s a m in a t o l ’i n t e r o array:

® - X ^ -X

W '.|/

''¢ /'

©
708 C a p it o l o 13

A n c h e q u e s to è u n a lg o ritm o 0 ( m)? S ì , p e rc h é in a lc u n i casi la c o rris p o n d e n z a p o trà


essere in d iv id u a ta alla fin e d e ll’array. In o ltre , n e l caso in c u i l’e le m e n to n o n sia p re se n te
n e ll’array, b iso g n a v isitare tu tti i su o i e le m e n ti.

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

O sse rv a n d o i v a lo ri, è o v v io n o ta re c h e q u e llo p iù fre q u e n te è 7, m a se !’a rray avesse


m ig liaia di v alo ri?

8 7 5 7 7 5 4 1 3 2 4 9 12 3 2 5 11 9 2 3 7 8

P o tre m m o c o n ta re q u a n te v o lte r ic o r r e il v alo re 8, p e r p o i passare a c o n ta re q u a n te v o lte


r ic o r r e il v alo re 7, e così via. A d ese m p io , n el p r im o array il n u m e ro 8 c o m p a re u n a vo lta
e il n u m e ro 7 c o m p a re tre v o lte. M a d o v e m e m o riz z ia m o q u esti c o n te g g i? In se ria m o li
in u n s e c o n d o array, counts, av e n te la stessa lu n g h e z z a del p rim o .

8 7 5 7 7 5 4

counts: 2 2
1 3 3 3 1

P oi, c e rc h ia m o il v alo re m a ssim o d e ll’array c h e c o n tie n e i c o n te g g i, c io è counts.Tale valo re


m a ssim o è 3, d o p o d ic h é c e rc h ia m o la p rim a p o siz io n e n e ll’array counts in c u i c o m p a re
il n u m e ro 3 e id e n tific h ia m o il v alo re c h e si trova nella m e d e sim a p o siz io n e n e ll’array a:
q u e llo è il v alo re p iù fre q u e n te in a, in q u e s to caso 7.
C e rc h ia m o , p e r p rim a cosa, di v alu tare il te m p o n e c e ssa rio p e r ca lc o lare i c o n te g g i.

for (int i = 0; i < a.length; i++)


{
counts[i] = . . . // conta quante volte a[i] compare in a
}

A n c h e in q u e s to caso v isitia m o ciasc u n e le m e n to u n a sola v o lta, m a in c o rris p o n d e n z a di


Un ciclo che esegue n iterazioni,
o g n i visita il la v o ro sv o lto è d e c is a m e n te m a g g io re , p e rc h é , c o m e av ete visto n el p ara g ra fo
ciascuna delle quali richiede
p re c e d e n te , o g n i a z io n e di c o n te g g io è 0{n). Im p ie g a n d o u n te m p o 0{ti) in ciasc u n a
un tempo 0(n), è 0{n^).
visita, il te m p o di e s e c u z io n e c o m p le ssiv o d e ll’a lg o ritm o è 0{n~).
Q u e s to a lg o ritm o è c o s titu ito da tre fasi:

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

A b b i a m o a p p e n a v is t o c h e la p r im a fa se è 0 ( m“). P er tro v a re il v a lo r e m a s s im o tra n c o n t e g g i


s e r v e u n t e m p o 0 { n ) : b a sta r ic o r d a r e l ’a l g o r i t m o v i s t o n e l P a r a g r a fo 7 . 3 . 3 e n o t a r e c h e
o g n i p a s so r i c h i e d e u n t e m p o d i e la b o r a z i o n e c o s t a n t e . I n f in e , a b b ia m o a p p e n a v i s t o c h e
la r ic e r c a d i u n v a lo r e r i c h i e d e u n t e m p o 0 { n ) .
C o m e p o s s i a m o s tim a r e il t e m p o d i e s e c u z i o n e c o m p l e s s i v o a p a r tir e d a lle s t im e
La stima 0-grande del tempo
d e l le s i n g o l e fasi? Il t e m p o t o t a le è , o v v i a m e n t e , u g u a l e alla s o m m a d e i s i n g o l i t e m p i , m a ,
necessario per eseguire più fasi in
u s a n d o le s t i m e O - g r a n d e , p r e n d i a m o la s tim a m a s s i m a tra q u e l l e d e l le s i n g o l e fa si. P er
o p n O lè pari al valore di 0-grande '
avente crescita più rapida tra quelli c a p ir e p e r c h é f a c c ia m o q u e s t o , i m m a g in a t e d i a v er i n d iv id u a t o le e q u a z i o n i c h e d e s c r iv o n o

relativi alle singole fasi. ' e f f e t t i v a m e n t e l ’a n d a m e n t o d e i t e m p i d i e s e c u z i o n e d e l le s i n g o l e fa si, in f u n z i o n e d i rt:

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 ~ ) .

13.7.3 Lo schema triangolare


C e r c h i a m o d i r e n d e r e p i ù v e l o c e l ’a l g o r i t m o v i s t o n e l p a r a g r a fo p r e c e d e n t e , p e r c h é c i
s e m b r a c h e p e r d a t e m p o n e l c o n t a r e g li s te s s i e l e m e n t i p iù v o l t e .

©
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 .

13.7.4 Algoritmi logaritmici


L e S tim e t e m p o r a li l o g a r i t m i c h e s o n o s o l i t a m e n t e r e la tiv e ad a l g o r i t m i c h e a o g n i p a s s o
s u d d iv i d o n o il p r o b le m a a m e tà , c o m e a b b ia m o v is t o , p e r e s e m p i o , n e l c a s o d e g li a lg o r it m i
d i r ic e r c a b in a r ia e d i o r d i n a m e n t o p e r f u s io n e .
In p a r t ic o la r e , q u i n d i , s e u n a d e l le fasi d i u n a l g o r i t m o è u n a r ic e r c a b in a r ia o u n
In algoritmo che a ogni passo divide
o r d i n a m e n t o p e r f u s io n e , n e lla s tim a O - g r a n d e d e l s u o t e m p o d i e s e c u z i o n e c o m p a r ir à
a metà il problema viene eseguito
u n lo g a r i t m o .
in un tempo 0(log(r?)).
A n a li z z i a m o g li e f f e t t i d i q u e s ta n u o v a id e a p e r m iglic:)rare l ’a l g o r i t m o p r e c e d e n t e ,
c h e c e r c a , in u n array, l ’e l e m e n t o c h e r ic o r r e p iù f r e q u e n t e m e n t e . S u p p o n i a m o d i o r d i n a r e
!’a rray p r im a d i e s e g u ir e l ’a l g o r i t m 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 .

2 1 . Q u a l è lo s c h e m a d i visita “ a la m p a d in e ” d e l s e g u e n t e a lg o r itm o , c h e v e r ific a se u n array


è u n p a lin d r o m o ?

for (int i = 0; i < a.length / 2; i++)


{
if (a[i] != a[a.length - 1 - i]) { return false; }
}
return true;

2 2 . Q u a l è la stim a O - g r a n d e d e l t e m p o di e s e c u z i o n e d e l s e g u e n t e a lg o r itm o , c h e v e r ific a


se il p r im o e le m e n t o di u n array è d u p lic a to al s u o in te r n o ?

for (int i = 1; i < a.length; i++)


{
if (a[0] == a[i]) { return true; }
}
return false;

2 3 . Q u a l è la stim a O - g r a n d e d e l t e m p o di e s e c u z i o n e d e l s e g u e n t e a lg o r itm o , c h e v e r ific a


se u n array c o n t i e n e (a lm e n o ) u n e le m e n t o d u p lic a to ?
O r d in a m e n t o e r ic e r c a 713

for (int i = 0; i < a.length; i++)


{
for (int 3 = i + 1; j < a.length; j++)
{
if (a[i] == a[j]) { return true; }
}
}
return false;

24. D e s c riv e te u n a lg o ritm o c h e v e rific h i se u n array c o n tie n e (a lm e n o ) u n e le m e n to


d u p lic ato e ch e venga eseg u ito in u n te m p o 0(n log(«)).
25. Q u al è la stim a O -g ra n d e del te m p o di esecu zio n e del se g u en te alg o ritm o , ch e cerca u n
ele m e n to (value) in u n array b id im en sio n ale n x nì

for (int i = 0; i < n; i++)


{
for (int 3 = 0; 3 < n; j++)
{
if (a[i][j] == value) { return true; }
}
}
return false;

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 OrdiQamsniP e ricercajj,fiJa liiirsiiajaia i'vf.. .Hrs-Af

Q u a n d o sc riv e te p ro g ra m m i in Java n o n av ete b iso g n o di im p le m e n ta re a lg o ritm i di


o r d in a m e n to : le classi Arrays e Collections c o n te n g o n o m e to d i di o r d in a m e n to e ric e rc a ,
p re se n ta ti n e i p ro ssim i p ara g ra fi.

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);

T ale m e to d o so rt usa l’a lg o ritm o q u ic k s o rt p re se n ta to nella s e z io n e A rg o m e n ti av an zati


13.3.
Se i dati da ordinare sono contenuti in un oggetto di tipo ArrayList si usa invece il
La classe C ollections contiene
un metodo di ordinamento
metodo sort della classe C o llec tio n s, che implementa l’algoritmo di ordinamento per
che agisce su vettori. fusione:

ArrayList<String> names = . . .;
C o llec tio n s. sort(nam es);

13.8.2 Ricerca binaria


Le classi Arrays e C o lle c tio n s contengono anche metodi statici, binarySearch, che
implementano l’algoritmo di ricerca binaria, con l’aggiunta di un utile miglioramento:
se il valore cercato non è presente nell’array, il valore restituito non è —1, ma —k — 1,
dove k è h posizione in cui dovrebbe essere inserito il valore, se lo si volesse inserire.
Ad esempio:

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

13.8.3 Confronto di oggetti


Nei programmi applicativi si ha spesso bisogno di ordinare raccolte di oggetti, per poi farvi
[metodo sort della classe Arrays
ordina oggetti di classi
ricerche. Per questo motivo le classi Arrays e C o llec tio n s contengono anche metodi sort
che implementano e binarySearch che operano su raccolte di oggetti, anche se non sanno come confrontare
l'interfaccia Comparable. tra loro oggetti di tipo arbitrario. Immaginiamo, ad esempio, di avere un array di oggetti
di tipo Country. Non è per niente ovvio decidere come ordinare nazioni: per nome o
sulla base della loro superficie? I metodi sort e binarySearch non possono prendere una
simile decisione per conto vostro. Infatti, richiedono che gli oggetti appartengano a classi
che implementano l’interfaccia Comparable, presentata nel Paragrafo 10.3 e dotata di un
unico metodo:.

public in terface Comparable<T>


{
in t compareTo(T other);
}

L’invocazione

a.compareTo(b)

deve restituire: un numero negativo se a precede b; un numero positivo se a segue b; zero


se a e b sono uguali.
Osservate che Comparable è un tipo generico, come ArrayList. Nel caso di un oggetto
ArrayList, il parametro di tipo specifica il tipo degli elementi del vettore. Nel caso di
Comparable, il parametro di tipo è il tipo del parametro ricevuto dal metodo compareTo,
quindi solitamente una classe che implementa Comparable vorrà essere “confrontabile con
se stessa”. Ad esempio, la classe Country implementa Comparable<Country>.
URDINAMENTO E RICERCA / ID

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:

public c la ss Country implements Comparable<Country>


{
public in t compareTo(Country other)
{
return Double.compare(area, o th e r .area);
}
}

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

A rrays.sort(coun tries); / / ordina per superficie crescente

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

. _ ______ _____

Il metodo compareTo può restituire qualsiasi numero intero, non solo - 1 , 0 o 1


Quando l’invocazione a.compareTo(b) deve segnalare che a precede b può restituire
qualsiasi numero intero negativo, non necessariamente il valore -1. Di conseguenza,
la verifica
i f (a.compareTo(b) == - l ) / / ERRORE !

è, in generale, errata. Dovete scrivere, invece


i f (a.compareTo(b) < O) / / c o sì va bene

Perché mai un metodo compareTo dovrebbe voler restituire un numero diverso da - 1 , 0


O 1? A volte è comodo restituire semplicemente la differenza tra due numeri interi. Ad
esempio, il metodo compareTo della classe String confronta i caratteri che si trovano in
posizioni corrispondenti:
char C l = charA t(i);
char c2 = o th e r .charA t(i);

Se i caratteri sono diversi, allora il metodo può semplicemente restituire la loro differenza:

if (cl != c2) { return cl - c2; }

Se Cl precede c2 questa differenza è certamente un numero negativo, ma non è


necessariamente il numero —1.
Si osservi che la restituzione di una differenza funziona soltanto se non si verifica
una condizione di overflow (Suggerimenti per la programmazione 10.1).

r Argomenti avanzati 1 3 . 4 ____________ ____________ _____________ ____


Linterfaccìa Comparator
A volte si vuole ordinare un array o un vettore di oggetti che non appartengono a una
classe che implementa l’interfaccia Comparable, oppure si vuole ordinare !’array in un modo
diverso da quello indotto dal metodo compareTo: ad esempio, può darsi che si vogliano
ordinare nazioni per nome invece che per superficie.
Non vorreste essere costretti a modificare il codice di una classe soltanto per poter
invocare A rrays.sort e, fortunatamente, esiste un’alternativa. Una versione del metodo
Arrays. so rt non richiede che gli oggetti da ordinare appartengano a una classe che realizza
l’interfaccia Comparable: potete fornire oggetti di qualsiasi tipo, ma dovete anche fornire
un comparatore di oggetti, che ha il compito, appunto, di confrontare gli oggetti che volete
ordinare. L’oggetto comparatore deve appartenere a una classe che implementa l’interfaccia
Comparator: ha un unico metodo, compare, che confronta due oggetti.
Come Comparable, l’interfaccia Comparator è un tipo parametrico, il cui parametro di
tipo specifica di che tipo sono i due parametri ricevuti dal metodo compare. Ad esempio,
l’interfaccia Comparator<Country> è questa:
O rdinamento e ricerca 717

public in terface Comparator<Country>


{
in t compare(Country a, Country b);
}

Se comp è un oggetto di una classe che implementa Comparator<Country>, !’invocazione


comp.COtnpare(a, b)

deve restituire: un numero negativo se a precede b; un numero positivo se a segue b; zero


se a e b sono uguali.
Ad esempio, ecco una classe Comparator per nazioni:

public c la ss CountryComparator implements Comparator<Country>


{
public in t compare(Country a, Country b)
{
return Double.compare(a.getArea(), b.getA rea());
}
}

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());

NotejifirJmS 13J.„ - „ . _______ _________„ ..


Comparatori definiti mediante espressioni lambda
Nelle versioni precedenti a Java 8 la definizione di un comparatore era abbastanza scomoda:
bisognava definire una classe che implementasse l’interfaccia Comparator, realizzandone il
metodo compare, per poi costruire un esemplare di tale classe. Una vera sfortuna, perché
in realtà i comparatori sono molto utili e si usano in diversi algoritmi, per fare ricerche,
ordinare dati e trovare il massimo o il minimo in un insieme.
Con l’avvento delle espressioni lambda, la definizione di un comparatore è diventata
molto più semplice. Ad esempio, per ordinare un array di parole sulla base della loro
lunghezza, si può scrivere:

Arrays.sort(words, (v, w) -> v . length() - w .len gth ());

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:

Arrays.sort(words. Comparator.comparing(w -> w .len g th ()));

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

Il metodo Comparator.comparing è in grado di gestire molti casi.Ad esempio, per ordinare


nazioni in base alla loro superficie, è sufficiente scrivere:
A rrays.sort(coun tries. Comparator.comparing(c -> c.g e tA r ea ()));

Ijemipi completi 13.1


Ù Migliorare Talgoritmo di ordinamento per inserimento
Problema. Implementare un algoritmo (chiamato ordinamento di Shell dal nome del
suo inventore, Donald Shell) che migliora l’algoritmo di ordinamento per inserimento
visto nella sezione Argomenti avanzati 13.2.
L’ordinamento di Shell migliora l’ordinamento per inserimento sfruttando il fatto che
tale algoritmo è 0{n) quando !’array da ordinare è, in realtà, già ordinato. L’ordinamento
di Shell ordina alcune porzioni dell’array, poi esegue l’ordinamento per inserimento
sull’intero array: in questo modo, tale ordinamento conclusivo non ha poi tanto lavoro
da fare.
Una fase chiave dell’ordinamento di Shell consiste nel disporre la sequenza da ordinare
in una tabella, con righe e colonne, per poi ordinare separatamente ciascuna colonna. Ad
esempio, se !’array è questo:

65 46 14 52 38 2 96 39 14 33 13 4 24 99 89 77 73 87 36 81

e lo disponiamo in una tabella di quattro colonne, otteniamo:

65 46 14 52

38 2 96 39

14 33 13 4

24 99 89 77
73 87 36 81

Ora ordiniamo le singole colonne:

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

Quindi, per ordinare un array di 20 elementi, partiamo con l’ordinamento di 13 colonne,


per poi passare a 4 colonne e, infine, a una sola colonna. Questa sequenza è quasi efficiente
quanto la migliore che si conosca, ma è più facile da calcolare.
In realtà, non si modifica effettivamente il contenuto dell’array durante ogni fase,
bensì si manipolano le posizioni degli elementi di ciascuna colonna.
Se, ad esempio, il numero di colonne, c, è uguale a 4, gli elementi delle quattro colonne
sono posizionati nell’array in questo modo:

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;
}

Mettendo tutto insieme, otteniamo il metodo seguente:

Ordina una colonna usando l'ordinamento per inserimento.


@param a !'array da ordinare
@param k l'in d ic e del primo elemento d e lla colonna
@param c lo spazio tra elementi d ella colonna
*/
public s t a t ic void in se r tio n S o r t(in t[] a, in t k, in t c)
{
for (in t i = k + c; i < a . length; 1 = i + c)
{
in t next = a [ i] ;
/ / sposta in avanti t u t t i g l i elementi maggiori
in t j = i;
while (j >= C && a [j - c] > next)
{
a [j] = a [j - c ];
j = j - c;
}
/ / in s e r is c i l'elem ento
a [j] = next;
}
}

A questo punto tutto è pronto perché possiamo implementare l’algoritmo di ordinamento


di Shell. Per prima cosa dobbiamo calcolare quanti elementi della sequenza di numeri di
colonne ci servono. Quindi, generiamo i numeri di tale sequenza, uno dopo l’altro, fino
a quando non viene superata la dimensione della lista da ordinare:
ArrayList<Integer> columns = new ArrayList<Integer>();
in t C = 1;
while (c < a . length)
{
columns.add(c);
C = 3 * C + 1;

}
O rdinamento e ricerca 721

Per o g n i valo re di columns d o b b ia m o ord in are il c o n t e n u t o di tu tte le s in g o le c o lo n n e :

for (in t S = columns.s iz e ( ) - l; s >= 0; s - - )


{
C = columns.get(s );
for (in t k = 0; k < c; k++)
{
in sertion S ort(a, k, c);
}
}

Le p r e sta zio n i s o n o b u o n e ? C o n fr o n tia m o le c o n q u e lle d el m e to d o A rrays.sort d ella


lib reria d i Java:

in t[] a = ArrayUtil.randomintArray(n, lOO);


in t[] a2 = Arrays.copyOf(a, a .length);
in t[] a3 = Arrays.copyOf(a, a .length);

Stopwatch timer = new StopWatch();

tim er .sta rtO ;


Sh ellS orter. s o r t(a );
tim er.stopO ;

System.o u t.p r in tIn("Elapsed time with Sh ell sort: "


+ tim er.getElapsedTimeO + " m illiseconds");

timer.resetO;
timer.startO;
A rrays.sort(a2);
tim er.stopO ;

System .out.println("Elapsed time with A rrays.sort: "


+ tim er.getElapsedTimeO + " m illiseconds");

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 ;

System.out.printIn("Elapsed time with in sertion sort: "


+ tim er.getElapsedTimeO + " m illiseconds");

C i s ia m o a ssic u r a ti c h e i tre a lg o r it m i o r d in in o lo ste ss o array, f a c e n d o c o p i e di


q u e llo r ie m p ito di v a lo ri casuali; in o ltr e , a b b ia m o v e r ific a to c h e il risu lta to p r o d o tto
daH’o r d in a m e n to di S h e ll sia c o r r e tto , c o n fr o n ta n d o lo c o n q u e llo p r o d o tto da Arrays,
so r t. In fin e , lo c o n fr o n tia m o a n c h e c o n l ’o r d in a m e n to p e r in s e r im e n to .
I risultati m ostran o c h e l’o r d in a m e n to di Sh ell è d e c isa m en te p iù v e lo c e d e ll’o r d in a m e n to
p e r in s e r im e n to :
722 C apitolo 13

Enter array siz e : IOOOOOO


Elapsed time with Shell sort: 205 m illiseconds
Elapsed time with A rrays.sort: 101 m illiseconds
Elapsed time with in sertion sort: 148196 m illiseconds

L’algoritmo quicksort (usato da A rrays.sort) batte, però, l’ordinamento di Shell e per


questo motivo quest’ultimo praticamente non viene utilizzato, anche se è un algoritmo
interessante, con un’efficienza che lascia un po’ sorpresi.
Potete fare qualche esperimento anche usando i numeri di colonne individuati nel
lavoro originale di Shell. Basta sostituire, nel metodo S h e llS o r te r .so r t, l’enunciato:

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.

Descrizione dell'dlgoritmo di ordinamento per selezione


• L’a lg o r itm o di o r d in a m e n to p er se le z io n e ordina u n array c erca n d o r ip etu ta m en te l’e le m e n to
m in im o della z o n a term in a le n o n ancora ordinata, sp o sta n d o lo all’in iz io della z o n a stessa.

Misurazione del tempo di esecuzione di un metodo


• Per m isurare il te m p o di e s e c u z io n e di u n m e to d o , c h ie d e te l ’ora al sistem a su b ito p rim a e
su b ito d o p o l’in v o c a z io n e d el m e to d o stesso.

Utilizzo della notazione O grande per descrivere il tempo di esecuzione di un algoritmo


• N e l l ’in fo r m a tic a te o rica si d escriv e il tasso di crescita di una fu n z io n e u san d o la n o ta z io n e
O -g r a n d e .
• L’o r d in a m e n to p er se le z io n e è un a lg o ritm o 0 { n ^ ) : il ra d d op p io della d im e n sio n e d e ll’in siem e
di dati q u ad ru p lica il te m p o di ela b o ra zio n e.
• L’o r d in a m e n to p er in se r im e n to è u n a lg o r itm o 0 ( « 2 ) .

Descrizione dell'algoritmo di ordinamento perfusione (mergesort)


• L’a lg o r itm o di o r d in a m e n to p er fu sio n e ord in a u n array d iv id e n d o lo a m età, o r d in a n d o ri­
co rsiv a m e n te ciascu n a d e lle d u e parti e fo n d e n d o , p o i, le d u e m età ordinate.

Tempi di esecuzione degli algoritmi di ordinamento per fusione e per selezione


• L’o r d in a m e n to p er fu s io n e è un a lg o r itm o 0 { t i lo g (//)). La fu n z io n e ti lo g (/i) cresce m o lto
p iù len ta m e n te di n~.
O rdinamento e ricerca 723

Tempi di esecuzione degli algoritm i di ricerca lineare e di ricerca binaria


• La ricerca lineare esamina tutti i valori di un array fino a trovare una corrispondenza con
quanto cercato o la fine dell’array.
• La ricerca lineare trova un valore in un array eseguendo un numero di passi 0{tt).
• La ricerca binaria cerca un valore in un array ordinato determinando se si può trovare nella
prima o nella seconda metà dell’array, ripetendo poi la ricerca in una delle due metà.
• La ricerca binaria trova la posizione di un valore in un array eseguendo un numero di passi
0(log(»i)).
Stime dell'andamento O grande delle prestazioni di algoritm i
• Un ciclo che esegue n iterazioni, ciascuna delle quali è costituita da un numero fisso di azioni,
richiede un tempo 0{n).
• Un ciclo che esegue n iterazioni, ciascuna delle quali richiede un tempo 0(/;), è 0{tr).
• La stima O-grande del tempo necessario per eseguire più fasi in sequenza è pari al valore di
O-grande avente crescita più rapida tra quelli relativi alle singole fasi.
• Un ciclo che esegue n iterazioni, la /-esima delle quali richiede un tempo 0(i), è 0{n~).
• Un algoritmo che a ogni passo divide a metà il problema viene eseguito in un tempo 0(log(«)).
Utilizzo dei metodi della libreria di Java per ordinare dati e fare ricerche
• La classe Arrays contiene il metodo di ordinamento che dovrebbe essere normalmente uti­
lizzato nei programmi Java.
• La classe Collections contiene un metodo di ordinamento che agisce su vettori.
• 11 metodo sort della classe Arrays ordina oggetti di classi che implementano l’interfaccia
Comparable.

Elgjiienti dì lìbrgri)|jre;$entatì in questo capìtolo


java.lang.System java.util.Collections
currentTimeMillis binarySearch
java.util.Arrays sort
binarySearch java .util. Campara t o K T>
sort compare
comparing

Esercizi di riepiiogo e^pr«#lMliinen|;d ,


* R13.1. Qual è la differenza fra cercare e ordinare?
R13.2. Attenzione al rischio di errori per scarto di uno. Scrivendo l’algoritmo di ordinamento per
selezione visto nel Paragrafo 13.1, occorre, come al solito, scegliere tra < e <=, tra a.length e
a.length - 1, e tra from e from + i: un terreno fertile per la proliferazione degli errori per scarto di
uno. Eseguite passo dopo passo il codice dell’algoritmo applicato ad array di lunghezza 0,1,2 e 3,
controllando accuratamente che tutti i valori degli indici siano corretti.
R13.3. Qual è l’andamento di crescita, in termini di O-grande, di queste funzioni?
a. + 2 /1 + 1
b. n10 + 9ri^ + IOn^ + \45rP
c. (n + 1)^
d. ( / r + //)“
724 C apitolo 13

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)

e, posto J{n) = rt-y confrontateli con


/(2000) / / ( 1000)
/4000)//(1000)
/ 10000) / / 1000)

* 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.

0(«) o(«=) 0(n>) 0{ti log tt) 0(2")


1000 5 5 5 5 5
2000
3000 45
10000

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.

R 1 3 . i l . Considerate il problema di trovare l’elemento più frequente in un array di lunghezza


n. Ecco tre possibili approcci:

a. Ordinare !’array, poi trovare la serie più lunga.


b Creare un array di contatori avente la stessa dimensione delTarray originario. Per ciascun
elemento, scandire l’intero array contando il numero di elementi uguali a quello in esame,
aggiornando il relativo contatore. Quindi, trovare il contatore che ha il valore massimo.
C Usare variabili per memorizzare l’elemento visto più frequentemente finora e la sua frequenza.
Per ogni indice i, verificare se a[i] è presente nelle posizioni a[o] . . . a[i - i]. Se non è
presente,contare quante volte è presente in a [i + l] . . . a [ n - i ] . S e a [ i ] ricorre più volte
dell’elemento visto più frequentemente finora, aggiornare le variabili.
Descrivete !’efficienza dei tre approcci in termini di O-grande.

R 1 3 . 1 2 . Seguite, passo dopo passo, l’esecuzione dell’algoritmo di ordinamento per selezione


applicato a questi due array:

a. 4, 7, 11, 4, 9, 5, 11, 7, 3, 5
b. - 7 , 6, 8, 7, 5, 9, 0, 11, 10, 5, 8

R 1 3 . 1 3 . Seguite,passo dopo passo, l’esecuzione dell’algoritmo di ordinamento per fusione applicato


a questi due array:

a. 5, 11, 7, 3, 5, 4, 7, 11, 4, 9
b. 9, 0, 11, 10, 5, 8, -7, 6, 8, 7, 5

R13.14. Seguite, passo dopo passo, l’esecuzione dei seguenti algoritmi:

a. Ricerca lineare di 7 in -7, i, 3, 3, 4, 7, i i , 13


b. Ricerca binaria di 8 in -7, 2, 2, 3, 4, 7, 8, i l , 13

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

allora deve essere modificato in modo da contenere


4 7 11 9 5 3

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.

Qual è la sua efficienza, in termini di O-grande?


R13.22. L’algoritmo di ordinamento per radice {radix sort) ordina un array di n numeri interi, cia­
scuno avente d cifre in formato decimale, usando dieci array ausiliari associati agli indici da 0 a 9.
Per prima cosa, inserisce ciascun valore u dell’array nell’array ausiliario il cui indice corrisponde
all’ultima cifra di v, quindi trasferisce nuovamente tutti gli elementi nell’array originale, preser­
vando il loro ordine (cioè prima trasferisce gli elementi dall’array ausiliario 0, poi quelli dall’array
ausiliario 1, e così via). Quindi la procedura viene ripetuta usando la penultima cifra di ciascun
valore come indice per decidere !’array ausiliario di destinazione, e così via. Qual è l’efficienza di
questo algoritmo in termini di O-grande, in funzione di n e di d? In quali casi questo algoritmo
è da preferirsi rispetto all’ordinamento per fusione?
** R13.23. Un algoritmo di ordinamento stabile {stable sort) non modifica l’ordine relativo di elementi
che hanno lo stesso valore, una caratteristica utile e richiesta in molte applicazioni. Considerate,
O rdinamento e ricerca 727

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]

E13.12. Scrivete un programma che ordini un esemplare di ArrayList<Country> in ordine decrescente,


in modo che la nazione di superficie maggiore si trovi all’inizio del vettore. Usate un Comparator.
E13.13. Considerate l’algoritmo di ricerca binaria del Paragrafo 13.6: se non trova l’elemento
cercato, il metodo search restituisce -1 . Modificatelo in modo che, se !’elemento non viene trovato,
venga invece restituito il valore -/e —1, dove ^ è la posizione in cui l’elemento dovrebbe essere
inserito (che è il comportamento di Arrays.binarySearch).
E13.14. Realizzate senza ricorsione il metodo sort dell’algoritmo di ordinamento per fusione,
nell’ipotesi che la lunghezza delTarray sia una potenza di 2. Prima fondete le regioni adiacenti di
dimensione 1, poi le regioni adiacenti di dimensione 2, quindi le regioni adiacenti di dimensione
4 e così via.
E13.15. Usate l’ordinamento per inserimento e la ricerca binaria dell’Esercizio E13.13 per ordinare
un array secondo quanto descritto nell’Esercizio R 13.20. Realizzate tale algoritmo e misuratene
le prestazioni.
E13.16. Scrivete una classe Person che implementi l’interfaccia Comparable, confrontando persone
in base al loro nome. Chiedete all’utente di inserire dieci nomi e generate dieci oggetti di tipo
Person. Usando il metodo compareTo, determinate la prima e l’ultima persona dell’insieme e
visualizzatele.
E13.17. Ordinate un vettore di stringhe in ordine crescente di lunghezza. Su^erimento: progettate
un apposito Comparator.
E13.18. Ordinate un vettore di stringhe in ordine crescente di lunghezza, in modo che stringhe
della stessa lunghezza vengano disposte in ordine lessicografico. Suggerimento: progettate un apposito
Comparator.

Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
14
Java Collections Framework

Obiettivi del capìtolo


• Imparare a utilizzare le raccolte di dati della libreria standard
• Saper usare gli iteratori per scandire raccolte
• Scegliere la raccolta più adeguata alla soluzione di ciascun problema
• Studiare applicazioni di pile e code

Se scrivete un programma che deve usare un contenitore di oggetti, potete scegliere


tra diverse alternative. Ovviamente potete usare un array o un vettore, ma gli scienziati
deirinformazione hanno sviluppato molti diversi meccanismi che possono svolgere
il compito richiesto in modo migliore. In questo capitolo presenteremo le classi e le
interfacce messe a disposizione dalla libreria di Java per descrivere e realizzare raccolte di
dati. Imparerete a usarle e a scegliere quella più adeguata a ciascun particolare problema.
111 Una panoramica del Collections Framework,
Una raccolta {collection)
Quando in un programma si ha la necessità di organizzare la memorizzazione di più oggetti,
raggruppa insieme elementi
li si può inserire in una raccolta (collection). La classe ArrayList presentata nel Capitolo 7
e ne consente il recupero successivo. è una delle molte classi che implementano raccolte e che sono messe a disposizione dei
programmatori dalla libreria standard di Java. In questo capitolo studierete, in particolare,
il Collections Framework di Java, una gerarchia di interfacce e classi aventi lo scopo di
realizzare contenitori di oggetti. Ogni interfaccia è implementata da una o più classi,
come si può vedere nella Figura 1.

« in te rfa c e» « in te rfa c e»
C o llec tio n Map

« in te rfa c e» « in te rfa c e» « in te rfa c e»


HashMap TreeMap
L ist Oueue Set

P riority
A rrayL ist Stack LinkedList HashSet TreeSet
Queue

Figura I Interfacce e classi del Java Collections Framework

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

Or^ R-^ Spi J Z

Figura 2 U n a lis ta d ilib ri FiguraB U n in s ie m e d ilib ri Figura 4 U n a p ila d ilib ri

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:

ArrayList<String> coll = new ArrayList<>();

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.

Tabella 1 Imetodi principali dell'interfaccia Collection


C ollection <S trin g> c o l l = new A rrayList<>(); La classe ArrayList im p lem en ta l’in te rfa cd a C ollection .
c o l l = new T r e e S e to O ; A n c h e la classe TreeSet (Paragrafo 1 4.3) im p lem en ta l’in terfaccia C ollection .
in t n = c o l l . s i z e O ; R e stitu isc e la d im e n sio n e della raccolta: ora n vale 0.
coll.add("H arry"); A g g iu n g e e le m e n ti alla c o lle z io n e .
c o ll.a d d (" S a lly " );
S tring S = c o ll.t o S t r in g O ; R e stitu isc e una stringa c o n te n e n te tutti gli e le m e n ti della raccolta: ora s è
u gu ale a [Harry, S a lly ].
System .o u t. p r i n t l n ( c o l l ) ; Invoca il m e to d o toS trin g e visualizza [Harry, S a lly ].
c o l l . remove("Harry"); E lim ina un e le m e n to dalla raccolta, restitu en d o f a ls e se l’e le m e n to n o n è
boolean b = coll.remove("Tom"); presente; b vale f a ls e .
b = c o ll.c o n ta in s (" S a lly " ); Verifica se la raccolta c o n tie n e l’e le m e n to cercato; b vale true.
fo r (S trin g s : c o l l ) C o n qualsiasi raccolta si p u ò usare il c ic lo for esteso. Q u e s to c ic lo visualizza
{ gli e le m e n ti su rig h e distinte.
System.o u t.p rin tln (s);
}
Iterator< String> i t e r = c o l l .i t e r a t o r ( ) ; Per visitare gli e le m e n ti di una raccolta si p u ò usare un iteratore (Paragrafo
1 4 .2 .3 ).
Java C ollections Framework 733

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.

14.2.1 La struttura delle liste concatenate


Per capire per quale motivo gli array siano inefficienti e ci sia bisogno di una struttura
più efficiente, immaginate un programma che gestisca una sequenza di oggetti che
rappresentano dipendenti, ordinati in base al cognome. Se un dipendente lascia il
lavoro, i suoi dati devono essere eliminati e, in un array, il buco che si crea nella
sequenza deve essere chiuso spostando tutti i dati che lo seguono.Viceversa, quando
viene assunto un nuovo dipendente bisogna inserire un oggetto nella sequenza:
il nuovo oggetto deve essere probabilmente inserito nella sequenza in qualche
posizione intermedia, per cui tutti gli oggetti che lo seguono devono essere spostati
verso la fine della sequenza. Spostare un elevato numero di elementi può comportare
Una lista concatenata è composta un notevole dispendio di tempo di elaborazione: una lista concatenata evita questi
I un certo numero di nodi, ciascuno spostamenti.
dei quali contiene un riferimento Una lista concatenata usa una sequenza di nodi, ciascuno dei quali memorizza un
ai nodo successivo. valore e un riferimento al nodo successivo nella sequenza (si veda la Figura 6).

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.

14.2.2 Laclasse LinkedList del Java Collections Framework


La libreria di Java mette a disposizione una classe che realizza una lista concatenata:
LinkedList nel pacchetto j a v a .u t il.E una classe generica, proprio come la classe ArrayList,
per cui il tipo degli elementi contenuti nella lista va specificato tra parentesi angolari, ad
esempio LinkedList<String> oppure LinkedList<Employee>.
LaTabella 2 presenta i metodi principali della classe LinkedList (ricordate che la classe
LinkedList eredita anche i metodi dell’interfaccia C o llec tio n , elencati nella Tabella 1).
Come potete vedere, ci sono metodi che consentono l’accesso diretto sia al primo
sia aH’ultimo elemento della lista, mentre per visitare gli altri elementi c’è bisogno di un
iteratore, di cui parleremo nel prossimo paragrafo.
J ava C ollections F ramework 735

Tabella 2 Alcuni metodi di LinkedList


LinkedList<String> l i s t = new LinkedList<>(); U n a lista v u ota.
l i s t . addLast( "Harry"); A g g iu n g e un e le m e n to alla fin e della lista, esattam en te c o m e add.
l i s t . addFirst ( "Sally" ); A g g iu n g e un e le m e n to all’in iz io della lista, d o p o d ic h é il c o n te n u to di l i s t
è [ S a lly , Harry].
lis t.g e tF ir s tO ; R estitu isce l’e le m e n to m em o r iz za to all’in izio della lista,in q u esto caso "Sally".
list.g e tL a stO ; R e stitu isc e l ’e le m e n to m e m o r iz z a to alla fin e della lista, in q u esto caso "Harry".
String removed = list.rem oveF irst(); E lim in a il p rim o e le m e n to della lista e lo restituisce, d o p o d ic h é il c o n te n u to
di l i s t è [Harry] e removed vale "Sally". Per elim in are l ’u ltim o e le m e n to si
usa, a n a lo g a m e n te, removeLast.
ListIterator<String> ite r = l i s t . IistIte r a to r (); R e s titu isc e un iteratore p er visitare tutti gli e le m e n ti della lista (per il su o
fu n z io n a m e n to si veda IaT abella 3).

14.2.3 Iteratori per liste


Un iteratore o cursore (iterator) rappresenta il concetto di posizione in un qualsiasi punto
Peraccedere agli elementi presenti
in una lista eoneatenata si usa
all’interno della lista concatenata. Concettualmente dovete considerare !’iteratore come
un iteratore (o eursore).
qualcosa che punta fra due elementi, esattamente come il cursore di un elaboratore di
testi punta fra due caratteri (Figura 9). In termini astratti,pensate a ciascun elemento della
lista come se fosse un carattere in un elaboratore di testi, mentre !’iteratore è il cursore
intermittente che si trova fra due caratteri.

Figura 9
Una visione astratta Posizione iniziale di L istIte r a to r
di un iteratore operante
su una lista

Dopo !'invocazione di next

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 next lancia !’eccezione NoSuchElementException se l’iteratore si trova già oltre la


fine della lista, per cui dovreste sempre invocare il metodo hasNext prima di invocare next:
restituisce true se c’è un elemento successivo.
i f (it e r a t o r .hasNextO)
{
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 ") ;

Potete visualizzare l’inserimento come se fosse la digitazione di testo in un elaboratore


di testi: ciascun carattere viene inserito dopo il cursore e, successivamente, il cursore si
sposta in modo da trovarsi dopo il carattere appena inserito (osservate la Figura 9). Gran
parte delle persone non ci fa caso: fate una prova e osservate attentamente in che modo
il vostro elaboratore di testi inserisce i caratteri.
Java C ollections F ramework 737

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 .

Tabella 3 Alcuni metodi delle interfacce Iterator e ListIterator


String S = iter.n extO ; Ip o tizzia m o ch e, prim a di questa in v o c a zio n e di next, i t e r puntasse all’in izio della
lista, il cu i c o n te n u to è [S a lly ]. D o p o l’e se c u z io n e , s vale "Sally" e l ’iteratore
punta alla fin e della lista.
iter.previousO ; Il m e to d o set a g giorn a l’u ltim o e le m e n to restitu ito da next o previous, d o p o ­
iter.setC 'D uliet"); d ic h é il c o n te n u to della lista è [ Iu lie t]. I m e to d i previous e set so n o dichiarati
n e ll’interfaccia L istiterator.
iter.hasNextO R e stitu isc e f a ls e p erch é l’iteratore si trova alla fin e della raccolta..
i f (iter.hasPreviousO ) Il m e to d o hasPrevious restituisce true p erch é l’iteratore n o n si trova all’in izio della
{ lista. I m e to d i previous e hasPrevious so n o dichiarati n e ll’in terfaccia ListIterator..
S = iter.previousO ;
}
iter.add("Diana"); A g g iu n g e u n e le m en to prim a della p o siz io n e d ell’iteratore, d o p o d ic h é il c o n te n u to
della lista è [Diana, Sally]. Il m e to d o add è dichiarato n e ll’interfaccia Listiterator.
iter.n extO ; Il m e to d o remove elim in a l ’u ltim o e le m e n to restitu ito da next o previous, d o p o ­
ite r . removeO; d ic h é il c o n te n u to della lista è [ S a lly ] .

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 ;

Programma che illu s tr a i l funzionamento d ella c la sse LinkedList.


738 C apitolo 14

*/
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");

/ / i l segno I nei commenti indica la posizione d e ll'ite r a to r e

ListIterator<String> iter a to r = s t a f f .li s t I t e r a t o r ( ) ; / / |DHRT


ite r a to r .n ext(); / / D|HRT
ite r a to r .n ext(); / / DH|RT

/ / aggiunge a l t r i elementi dopo i l secondo

ite r a to r.a d d (" lu lie t" ); / / DHl| RT


ite r a to r .add("Nina"); / / DH1N|RT

ite r a to r .n ext(); / / DHlNR|T

/ / to g lie l'u ltim o elemento r e s titu ito

iterator.removeO; / / DHlN| T

/ / v isu a lizza t u t t i g l i elementi

System.ou t. p r in tln (s ta ff);


System.o u t.p rin tln (" Expected: [Diana, Harry, l u l i e t , Nina, Torn]");
}
}

Esecuzione del programma


[Diana, Harry, l u l i e t , Nina, Tom]
Expected: [Diana, Harry, l u l i e t , Nina, Tom]

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

iter.ad d("E " );


i t e r , next 0 ;
iter.ad d (" F " );

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.

14.3.1 Scegliere unimplementazione di insieme


L’interfaccia Set della libreria standard di Java ha gli stessi metodi dell’interfaccia C ollection ,
L e d a s siH a s h S e te T re e S e t
implementano Hnterfacda S et.
elencati nellaTabella l,ma tra raccolte generiche e insiemi c’è una differenza essenziale:un
insieme non può contenere elementi duplicati. Se aggiungete a un insieme un elemento
già presente al suo interno, l’azione di inserimento viene ignorata.
Le classi HashSet e TreeSet implementano l’interfaccia Set e sono basate su due
Le implementazioni di insieme
dispongono gli elementi in modo
meccanismi diversi: la prima è una tabella hash (hash table), mentre la seconda è un
da poterli ritrovare velocemente.
albero di ricerca binario {binary search free). Entrambe le implementazioni dispongono
gli elementi al proprio interno in modo da rendere efficienti le operazioni di ricerca,
di inserimento e di rimozione di elementi, ma usano due strategie diverse.
L’idea su cui si basano le tabelle hash è semplice. Gli elementi dell’insieme sono
raggruppati in raccolte più piccole di elementi che condividono una stessa caratteristica:
potete immaginare un insieme di libri come costituito da un gruppo per ciascun possibile
colore, in modo che libri dello stesso colore vengano posti nello stesso gruppo. Per
scoprire se un determinato libro è presente nell’insieme, è sufficiente verificare la SLia
presenza tra i libri che appartengono al gruppo corrispondente al suo colore. Le tabelle
hash non usano proprio i colori, ma numeri interi (chiamati codici di hash) che possono
essere calcolati a partire dagli elementi.
^iomputer e socie!il4.1

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

14.3.2 Lavorare con insiemi


Per aggiungere e rimuovere elementi in un insieme si usano i metocii add e remove:

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")) . . .

Il metodo contains fa i confronti usando il metodo equals dell’elemento. Se !’insieme


contiene oggetti di tipo String o Integer non c’è da preoccuparsi, perché tali classi mettono
a disposizione un metodo equals adeguato, ma se il tipo dell’elemento è una classe realizzata
in proprio, questa deve aver definito il metodo equals, come descritto nel Paragrafo 9.5.2.
Infine, per elencare tutti gli elementi presenti nell’insieme si usa un iteratore. Come per
gli iteratori delle liste concatenate, si usano i metodi next e hasNext per scandire l’insieme,
un elemento per volta.

Iterator<String> it e r = names.ite r a to r ();


while (iter.h a sN ex tO )
{
String name = ite r .n e x t();
Elabora name
}

Invece di usare esplicitamente un iteratore si può usare un ciclo for esteso:


for(S trin g name : names)
{
Elabora name
}

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

Tuttavia, è possibile eliminare dall’insieme l’elemento che si trova nella posizione


deH’iteratore, proprio come si fa con gli iteratori di lista.
Ancora, l’interfaccia Ite ra to r non ha il metodo previous per tornare indietro nella
scansione degli elementi: dato che gli elementi non sono ordinati, non ha senso distinguere
tra “andare avanti” e ’’tornare indietro”.

Tabella 4 Lavorare con insiemi


Set<String> names; Per dichiarare variabili usate il tipo interfaccia.
names = new HashSet<>(); Se dovete visitare gli elementi dell’insieme in ordine usate TreeSet.
names.add("Romeo"); Ora names.sizeO vale 1.
names.add("Fred"); Ora names.sizeO vale 2.
names.add("Romeo"); names.sizeO vale ancora 2: non vengono aggiunti duplicati.
i f (names.contains("Fred")) Il metodo contains verifica se un valore appartiene all’insieme. In questo caso il metodo
restituisce true.
System.out.println(names); Visualizza !’insieme nel formato [Fred, Romeo]. Gli elementi non compaiono necessariamente
nell’ordine in cui sono stati inseriti.
for (String name : names) Usate questo ciclo per visitare tutti gli elementi dell’insieme.
{

}
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.

11 programma seguente mostra un’applicazione degli insiemi. Legge tutte le parole di un


dizionario, contenuto in un file, e le inserisce in un insieme; poi, legge tutte le parole di un
documento (in questo caso, il libro “Alice in Wonderland”, y4//ce nel paese delle meraviglie),
inserendole in un secondo insieme; infine, visualizza tutte le parole di tale secondo insieme
che non figurano nell’insieme contenente il dizionario delle parole corrette, identificando
in questo modo le parole potenzialmente scritte in modo errato (come potrete vedere,
usiamo un dizionario americano, quindi alcune parole inglesi, come clamour, vengono
segnalate come potenziali errori).

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

public s ta tic void main( Strin g[] args)


throws FileNotFoundException
{
/ / Legge i l dizion ario e i l documento

Set<String> dictionaryWords = readWords("words");


Set<String> documentWords = readWords(" alice3 0 .tx t" );

/ / V isualizza le parole presenti nel documento ma non nel dizionario

for (String word : documentWords)


{
i f ( ! dictionaryWords. contains(word))
{
System. ou t. prin tIn(word);
}
}
}
/**
Legge tu tte le parole contenute in un file.
@param filename i l nome del file
@return un insieme con tu tte le parole del file, convertite in minuscolo;
una parola è una sequenza di le tte r e , maiuscole o minuscole.
V
public s ta tic Set<String> readWords(String filename)
throws FileNotFoundException
{
Set<String> words = new FlashSet<>();
Scanner in = new Scanner (new File(filenam e));
/ / delim itatore: carattere che non appartiene a ll'in t e r v a llo a-z né A-Z
in . useD elim iter(" [^a-zA-Z]+");
while (in.hasN extO )
{
words. add( in . next( ) . toLowerCase() );
}
return words;

Esecuzione del programma


neighbouring
croqueted
pennyworth
dutchess
comfits
x ii
dinn
clamour
Java C ollections F ramework 745

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.

I Suggerimenti per la programmazione 14.1


Usare riferimenti a interfaccia per manipolare strutture dati
Memorizzare in una variabile di tipo Set un riferimento a un oggetto di tipo
...................

HashSet o
è considerato un buono stile di programmazione.
TreeSet

Set<String> names = new HashSet<>();

In questo modo, se deciderete in seguito di usare un oggetto di tipo TreeSet, dovrete


modificare soltanto una riga di codice.
Se un metodo può elaborare una raccolta di qualsiasi tipo, definite la sua variabile
parametro di tipo C ollection :

public s ta tic void removeLongWords(Collection<String> words)

In teoria dovremmo fare la stessa raccomandazione per l’interfaccia L ist, suggerendo di


memorizzare in variabili di tipo L ist i riferimenti a oggetti LinkedList o ArrayList.Tuttavia,
nella libreria di Java l’interfaccia L ist ha i metodi get e se t per l’accesso casuale, nonostante
questi metodi siano molto inefficienti per le liste concatenate. Non potete scrivere codice
efficiente se non sapete se i metodi che state invocando sono efficienti oppure no. Questo
è evidentemente un serio errore di progettazione della libreria standard e per tale motivo
non si può raccomandare l’uso dell’interfaccia L ist.

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<>();

Per aggiungere un’associazione alla mappa si usa il metodo put:


favoriteC olors. put( " lu lie t ", Color. RED);

Potete modificare il valore di un’associazione già esistente semplicemente invocando di


nuovo il metodo put:

favoriteC olors. put("1u l i e t ", Color.BLUE);

11 metodo get restituisce il valore associato a una chiave:


Color j u l i e t SFavoriteColor = fa v o r ite C o lo rs.g e t(" lu lie t" );

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

Tabella 5 Lavorare con m appe


Map<String, Integer> scores; Q u i le ch iavi so n o strin gh e e i valori so n o in volu cri di tip o Integer. Per dichiarare variabili
si usa il tip o interfaccia.
scores = new TreeMap<>(); S e n o n avete b iso g n o di visitare le ch iavi della m appa in o rd in e usate HashMap.
scores.put("Harry", 90); A g g iu n g e ch iavi e valori alla m appa.
scores.put("Sally", 95);
scores.put("Sally", IOO); M o d ific a il valore associato a una ch iave esisten te.
int n = scores.get("Sally"); Isp ezion a il valore associato a una chiave, o tte n e n d o nuli se la ch iave n o n è presente. Alla
Integer n2 = scores.get("Diana"); fin e n vale 100 e n2 vale nuli.
System.out. println(scores); V isualizza scores.toStringO , in q u esto caso la stringa {Harry=90, Sally=IOO).
for (String key : scores.keySet()) Il c ic lo scand isce tu tte le ch iavi della m appa e n el su o c o r p o si isp ez io n a n o tutti i valori
{ co rrisp o n d e n ti.
Integer value = scores.get(key);

}
scores. remove("Sally") ; E lim in a la ch iave e il su o valore.

Set<String> keySet = m.keySet();


for (String key : keySet)
{
Color value = m.get(key);
System.out.println(key + " : " + value);
}

Il programma che segue mostra una mappa in azione.

File MapDemo.java

import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

Questo programma collauda una mappa che associa nomi a colori.


*/
public class MapDemo
{
public static void main(String[] args)
{
MapcString, Color> favoriteColors = new HashMap<>();
favoriteColors.put("1uliet", Color.BLUE);
favoriteColors.put("Romeo", Color.GREEN);
favoriteColors.put("Adam", Color.RED);
favoriteColors.put("Eve", Color.BLUE);

// visualizza tutte le chiavi e i valori presenti nella mappa

Set<String> keySet = favoriteColors.keySet();


for (String key : keySet)
748 C apitolo 14

Color value = favoriteC olors.get(k ey);


System .out.printIn(key + " : " + value);

Esecuzione del programma

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:

Integer count = fr eq u e n c ie s.g et(word); / / recupera i l conteggio attu ale


/ / se word non c 'e ra , poni i l conteggio a 1, altrim en ti incrementalo
i f (count == n u ll) { count = I; }
e ls e { count = count + I; }
frequencies.put(word, count);

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

frequencies.m erge(word, 1, (oldValue, notPresentValue) -> oldValue + l) ;

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.

CftnsifllijjwlicLJ„4J_____________ ______________ ____________


Scegliere una raccolta
Immaginate di dover memorizzare oggetti in una raccolta; avete già visto un certo numero
di diverse strutture per i dati e questi “Consigli pratici” riassumono come si possa scegliere
la raccolta più appropriata per una determinata applicazione.

Fase 1. Determinate le modalità di accesso agli elementi


Gli elementi vengono memorizzati in un contenitore per poterli recuperare in seguito.
Come volete accedere ai singoli elementi? Ci sono diverse possibilità.

• Si accede agli elementi mediante una posizione, rappresentata da un numero intero:


usate un ArrayList.
• Si accede agli elementi mediante una chiave che non fa parte dell’oggetto: usate
una mappa.
• Non avete bisogno di accedere ai singoli elementi sulla base della loro posizione:
prenderete una decisione nelle Fasi 3 e 4.

Fase 2. Determinate il tipo degli elementi o i tipi di chiavi e valori


In una lista o in un insieme determinate il tipo degli elementi che volete memorizzare.
Ad esempio, se volete rappresentare un insieme di libri, il tipo degli elementi potrebbe
essere Book.
Analogamente, nel caso di una mappa determinate il tipo delle chiavi e il tipo dei
valori a esse associati. Se volete cercare libri in base a un codice identificativo (ID), potete
usare mappe di tipo Map<Integer, Book> oppure Map<String, Book>, in relazione al tipo di ID
che intendete usare.

Fase 3. Determinate se l’ordine degli elementi o delle chiavi è importante


Quando visitate gli elementi della raccolta o le chiavi della mappa, vi interessa l’ordine
con il quale avviene l’operazione? Ci sono diverse possibilità.

• 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 4. Determinate quali operazioni devono essere efficienti


Ci sono diverse possibilità.

• La ricerca di elementi deve essere efficiente: usate HashSet.


• Devono essere veloci l’inserimento e la rimozione di elementi nella posizione iniziale
O in posizioni intermedie, nelle quali si è già ispezionato l’elemento: usate LinkedList.
• Effettuate inserimenti soltanto nella posizione terminale e usate così pochi elementi
che la velocità non vi interessa: usate ArrayList.

Fase 5. Per insiemi e mappe con tabella hash, decidete se occorre realizzare i metodi equals e
hashCode

• Se i vostri elementi (o chiavi) appartengono a una classe realizzata da altri, controllate


se ha i propri metodi hashCode e equals: se è così, siete a posto. Questo accade per la
maggior parte delle classi della libreria standard di Java, come String, Integer, Rectangle
e così via.
• In caso contrario, decidete se potete confrontare gli elementi per identità, cosa che
avviene se non costruite mai due elementi distinti che abbiano lo stesso contenuto.
In tal caso non avete bisogno di far nulla: i metodi hashCode e equals della classe Object
sono adeguati.
• Altrimenti dovete realizzare vostri metodi hashCode e e q u a ls, quindi consultate il
Paragrafo 9.5.2 e la sezione Argomenti avanzati 14.1.

Fase 6. Se usate un albero, decidete se serve un comparatore


Osservate la classe degli elementi dell’insieme o delle chiavi della mappa: implementa
l’interfaccia Comparable? In tal caso, se l’ordinamento indotto dal metodo comparalo è quello
che volete, allora non avete bisogno di far nulla. Questo accade per molte classi della
libreria standard, in particolare per String e Integer.
In caso contrario, la classe dei vostri elementi deve implementare l’interfaccia Comparable
(come descritto nel Paragrafo 10.3) oppure dovete definire una classe che implementi
l’interfaccia Comparator, come visto nella sezione Argomenti avanzati 13.4.

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 1. Determinate le modalità di accesso agli elementi


In questo caso i valori sono le occorrenze delle parole e abbiamo un valore per ciascuna
parola, per cui vogliamo usare una mappa che gestisca il conteggio associato a una parola.

Fase 2. Determinate il tipo degli elementi o i tipi di chiavi e valori


Le parole sono di tipo String e i conteggi sono Integer (non possiamo usare int come tipo
parametrico perché è un tipo primitivo), quindi ci serve una mappa di tipo Map<String,
Integer>.

Fase 3. Determinate se l’ordine degli elementi o delle chiavi è importante


Ci viene chiesto di visualizzare le parole in ordine alfabetico, quindi useremo TreeMap.

Fase 4. Determinate quali operazioni devono essere efficienti


Saltiamo questa fase, perché abbiamo già deciso di usare una mappa.

Fase 5. Per insiemi e mappe con tabella hash, decidete se occorre realizzare i metodi equals e
hashCode

Saltiamo questa fase, perché abbiamo già deciso di usare TreeMap.

Fase 6. Se usate un albero, decidete se serve un comparatore


11 tipo delle chiavi della nostra mappa è String, che implementa l’interfaccia Comparable,
per cui non c’è altro da fare.
Abbiamo quindi scelto la nostra struttura di memorizzazione dei dati. Lo pseudocodice
per descrivere la soluzione del problema è veramente semplice:

Per ogni parola presente nel file d'ingresso


Elimina dalla parola i caratteri che non sono lettere.
Se la parola è già presente nella mappa dei conteggi
Incrementa il conteggio associato.
Altrimenti
Imposta il conteggio al valore 1.

Ecco il codice del programma.


752 C apitolo 14

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());

// ispeziona il conteggio attuale della parola

Integer count = frequencies.get(word);

// se la parola non c'era usa 1, altrimenti incrementa il conteggio

if (count == null) { count = I; }


else { count = count + I; }

frequencies.put(word, count);

// visualizza tutte le parole e il relativo conteggio

for (String key : frequencies.keySet())


{
System.out.printf("%-20s%l0d\n", key, frequencies.get(key));
}
}

Elimina da una stringa i caratteri che non sono lettere.


@param s una stringa
@return una stringa contenente tutte le lettere presenti in s
*/
public static String clean(String s)
{
strin g r =
for (int i = 0; i < s.length(); i++)
{
char C = s.charAt(i);
if (Character.isLetter(c))
{
Java C ollections F ramework 753

r = r + c;

}
return r.toLowerCase();

Argomenti avanzati 14.1


Funzioni di hash
Se usate un insieme o una mappa che siano realizzati mediante una tabella hash, è possibile
che dobbiate realizzare una funzione di hash. Una funzione di hash {hash function) è
una funzione che, a partire da un oggetto, calcola un valore intero, il codice di hash
{hash coc/e), facendo in modo che oggetti diversi abbiano codici di hash diversi con elevata
probabilità. Dal momento che la procedura di hashing è così importante, la classe Object
ha un metodo hashCode. L’invocazione

int h = x.hashCodeO;

calcola il codice di hash dell’oggetto x di tipo qualsiasi. Se volete memorizzare oggetti


di una determinata classe come elementi di un insieme di tipo HashSet o usarli come
chiavi in una mappa di tipo HashMap, la classe dovrebbe sovrascrivere questo metodo, con
un’implementazione che faccia in modo che oggetti diversi abbiano codici di hash diversi
con elevata probabilità.
Ad esempio, la classe String definisce una funzione di hash che, per le stringhe, svolge
Una funzione di hash calcola

I un numero intero a partire


da un oggetto.

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.

Stringa Codice di hash


Codici di hash di alcune
stringhe "eat" 100184
"tea" 114704
"Iuliet" -2065036585
"Ugh" 84982
"VII" 84982

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:

final in t HASH_MULTIPLIER = 31;


in t h = 0;
for (in t i = 0; i < s.le n g th (); i++)
{
h = HASH_MULTIPLIER * h + s.ch arA t(i);
}

Ad esempio, usando ovviamente i valori dei caratteri definiti dal codice Unicode, il
codice di hash di "eat" è:

31 * (31 * 'e' + 'a ') + 't' = 100184

11 codice di hash di "tea" è abbastanza diverso:

31 * (31 * 't' + 'e ') + 'a' = 114704

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();

}
}

Combinate quindi i due codici di hash.

final in t HASH^MULTIPLIER = 31;


in t h = HASH_MULTIPLIER * hi + h2;
return h;

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

I Il metodo hashCode deve essere


compatibile con il metodo e q u a ls .
devono avere lo stesso codice di hash.
Se una classe sovrascrive il metodo equals ma non il metodo hashCode ci saranno dei
problemi. Supponiamo di aver definito nella classe Country il metodo equals che dichiari
uguali due nazioni dopo averne confrontato il nome e la superficie, senza però sovrascrivere
il metodo hashCode: di conseguenza, la classe lo eredita dalla superclasse Object, che calcola
un codice di hash a partire d^Windirizzo in memoria dell’oggetto. L’effetto è una probabilità
molto alta che due qualsiasi oggetti abbiano codici di hash diversi pur avendo lo stesso
contenuto, nel qual caso un HashSet li memorizzerà come se fossero due oggetti distinti.
Tuttavia, se non definite né equals né hashCode, non ci sono problemi. Il metodo equals
della classe Object considera due oggetti uguali solo se hanno lo stesso indirizzo in memoria
e, quindi, sono in realtà lo stesso oggetto e hanno, conseguentemente, lo stesso codice
di hash. Perciò, la classe Object ha metodi equals e hashCode compatibili. Naturalmente in
questo caso il concetto di uguaglianza è molto limitato: due oggetti vengono considerati
uguali soltanto se sono lo stesso oggetto, un concetto di uguaglianza peraltro perfettamente
valido, dipende dall’applicazione.

14.5 Pile, code e code prioritarie


In questo paragrafo prenderemo in considerazione pile, code e code prioritarie, strutture
di memorizzazione dei dati che manifestano strategie diverse in relazione all’operazione
di rimozione, che elimina, rispettivamente, l’elemento inserito più recentemente, quello
inserito meno recentemente e quello avente la massima priorità.

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.

Stack<String> s = new Stack<>();


s.push("A");
s.push("B");
s.push("C");
while (s.siz e O > o)
{
System.out.print(s.pop() + " ");
} / / visualizza CBA
756 C apitolo 14

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).

Tabella 7 Stack<Integer> s = new Stack<>(); Costruzione di una pila vuota.


Lavorare con pile
s.pu sh (l); Aggiunta di elementi in cima alla pila; ora s contiene [ l , 2, 3]
s.push(2); (come il metodo toString della classe Stack, mostriamo la cima
s.push(3); della pila in fondo all’elenco del contenuto).
int top = s.popO; Eliminazione dell’elemento che si trova in cima alla pila; top
assume il valore 3 e ora s contiene [l, 2].
head = s.peek(); Ispezione dell’elemento presente in cima alla pila, senza ri­
muoverlo; top vale 2.

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

Oueue<String> q = new LinkedList<>();


q.add("A");
q.add("B");
q.addr'C");
w h ile ( q . s iz e O > O)
{
System.out.print(q.remove0 + " ”);
} / / visualizza ABC

Tabella 8 Oueue<Integer> q = new LinkedList<>(); La classe LinkedList implementa l’interfaccia Queue.


Lavorare con code
q.add(l); Aggiunta di elementi alla fine della coda; ora q contiene
q.add(2); [1, 2, 3].
q.add(3);

int head = q.remove(); Eliminazione dell’elemento che si trova all’inizio della


coda; head assume il valore I e ora q contiene [2, 3].
head = q.peek(); Ispezione dell’elemento presente all’inizio della coda,
senza rimuoverlo; head vale 2.

14.5.3 Code prioritarie


Una coda prioritaria {priority queue) è una raccolta di elementi, ciascuno dei quali ha
Quando si estrae un elemento
da una coda prioritaria viene eliminato
una priorità. Un esempio tipico di coda prioritaria è una raccolta di richieste di esecuzione
quello avente la priorità massima.
di lavori, alcune delle quali possono essere più urgenti di altre. Diversamente da una coda
ordinaria, la coda prioritaria non gestisce le rimozioni di elementi con una strategia di
tipo FIFO: gli elementi vengono rimossi sulla base della loro priorità. In altre parole, si
possono inserire nuovi elementi in qualunque ordine, ma quando si richiede la rimozione
di un elemento viene estratto quello avente la priorità massima.
C ’è la consuetudine di attribuire valori più bassi alle priorità più elevate, con la
priorità 1 che indica la massima urgenza. Di conseguenza, ogni operazione di rimozione
estrae dalla coda prioritaria l’elemento minimo.
Consideriamo, ad esempio, il codice seguente, nel quale aggiungiamo a una coda
prioritaria oggetti di tipo WorkOrder, che rappresentano richieste di esecuzione di lavori,
ognuna delle quali ha una priorità e una descrizione:

PriorityQueue<WorkOrder> q = new PriorityQueue<>();


q.add(new Work0rder(3, "Shampoo carpets"));
q.add(new WorkOrder(l, "Fix broken sink"));
q.add(new Work0rder(2, "Order cleaning supplies"));

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?

PriorityOueue<String> q = new PriorityOueue<>();


q.add("3 - Shampoo carpets");
q.add("l - Fix broken sink ");
q.add("2 - Order cleaning supplies ");

Perfarpratica
A questo punto si consiglia di svolgere gli esercizi R 14.15, E 14.8 e E 14.9, al termine
del capitolo.

Argomenti avanzati 14.2


Notazione polacca inversa
Negli Anni Venti il matematico polacco Jan Lukasiewicz comprese che era possibile
evitare !’utilizzo di parentesi nelle espressioni aritmetiche scrivendo gli operatori prima
dei propri operandi, ad esempio + 3 4 invece di 3 + 4.Trent’anni più tardi Pinformatico
australiano Charles Hamblin notò che si poteva ottenere una sintassi ancora migliore
Ja v a C ollections Fr am ew or k 759

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.

14.6 Applicazioni di pile e code


Nonostante la loro semplicità, le pile e le code sono strutture estremamente versatili. Nei
paragrafi che seguono vedremo alcune delle loro applicazioni più utili.
760 C apitolo 14

14.6.1 Accoppiamento delle parentesi


Nella sezione Errori comuni 4.2 avete visto un semplice trucco che consente di individuare
Per verificare se in un'espressione
le parentesi sono accoppiate
in una espressione la presenza di parentesi non accoppiate correttamente, ad esempio così:
correttamente si può usare una pila.
-(b (4 C) ) / (2 a)
1 2 10 1 0

Si incrementa un contatore ogni volta che si incontra una parentesi aperta e lo si


decrementa quando si incontra una parentesi chiusa. 11 contatore non deve mai diventare
negativo e al termine dell’espressione deve essere uguale a zero.
Questo trucco funziona per la verifica delle espressioni in Java, perché si usa un unico
tipo di parentesi, ma nella notazione matematica si possono avere più tipi di parentesi,
come in questo esempio:

- { 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:

Quando si trova una parentesi aperta, la si inserisce nella pila.


Quando si trova una parentesi chiusa, si rimuove un elemento dalla pila.
Se le parentesi aperta e chiusa non corrispondono
Le parentesi non sono accoppiate correttamente. Fine.
Seallafinela pila è vuota
Le parentesi sono accoppiate correttamente.
Altrimenti
Le parentesi non sono accoppiare correttamente.

Ecco un’esecuzione dell’algoritmo passo dopo passo, nell’analisi della semplice espressione
precedente:

Pila Espressione da esaminare Commenti


Vuota - { [ b* b - ( 4 ^a * c ) ] / (2* a ) l
{ [b*b-{4*aV)l/(2^a)l
U b^b-(4*a*c)l/(2*a)}
{[( 4 * a^ c ) ] / ( 2 ^ a ) }
{[ l / (2 " a) } Tonde accoppiate
{ / ( 2 * a) l Quadre accoppiate
{{ 2"a)l
{ } Graffe accoppiate
Vuota Dati terminati Graffe accoppiate
Accoppiamento corretto

14.6.2 Valutazione di espressioni RPN


Pensate a come si scrivono le espressioni aritmetiche, come (3 + 4) x 5: le parentesi
Per valutare espressioni
in notazione polacca inversa
sono necessarie perché 3 e 4 vengano sommati prima di moltiplicare per 5 il risultato
si può usare una liìla.
dell’addizione.
Ja v a C ollections Fr a m e w o r k 761

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:

Se è stato letto un numero


Inseriscilo nella pila.
Altrimenti se è stato letto un operatore
Estraiduevaloridalla pila.
Applica l'operatore ai due valori estratti.
Inserisci nella pila il risultato ottenuto.
Altrimenti se non ci sono più dati da leggere
Estrai dalla pila il risultato e visualizzalo.

La figura mostra, passo dopo passo, la valutazione dell’espressione 3 4 5 + x.

Pila Espressione da esaminare Commenti


Vuota 345 + x
3 45 + x Numeri inseriti nella pila
34 5+ x
345 + X
39 X Estrai 4 e 5 e impila 9, il risultato di 4 + 5
27 Dati terminati Estrai 3 e 9 e impila 27, il risultato di 3 x 9
Vuota Estrai e visualizza il risultato, 27

Il programma seguente simula il funzionamento di una calcolatrice in notazione polacca


inversa.

File Calculator.java

import java.u til.S can n er;


import Java.u t i l . Stack;

Questa c a lc o la tr ic e usa la notazione polacca inversa.


*/ '

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

boolean done = fa lse ;


while (!done)
{
String input = in.nextL in e();

11 se è un operatore, e s tr a i g l i operandi e impila i l r is u lta to

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

r e s u lts . push(Integer. p arsein t( input));


}
S ystem .ou t.p rin tln (resu lts);

14.6.3 Valutazione di espressioni algebriche


Nel paragrafo prececiente avete visto come sia possibile valutare espressioni in notazione
Usando due pile si possono valutare
espressioni nella notazione
polacca inversa usando una pila. Se quella notazione non vi è piaciuta, sarete lieti di sapere
algebrica convenzionale.
che si possono valutare anche espressioni nella notazione algebrica convenzionale usando
due pile, una per gli operandi e una per gli operatori.
Consideriamo prima un esempio semplice, come l’espressione 3 + 4. Inseriamo gli
operandi, 3 e 4, nella pila degli operandi e l’operatore, +, nella pila degli operatori. Poi,
estraiamo due operandi dalla pila degli operandi e un operatore dalla pila degli operatori,
eseguiamo l’operazione e inseriamo il risultato nella pila degli operandi.
La procedura appena descritta è il fulcro deH’algoritmo di valutazione di un’espressione
generica: la chiamiamo “valutazione della cima” (lasciando sottinteso “della pila”).
Nella notazione algebrica ogni operatore ha una ben definita precedenza: gli operatori
+ e —hanno la precedenza più bassa (e uguale tra loro), mentre X e / hanno la precedenza
più alta (e uguale tra loro).
Ja v a C ollections Fr a m e w o r k 763

Pila operandi Pila operatori Espressione Commenti


da elaborare
Vuota Vuota 3+4

O + 4

O 4

O Dati terminati Valuta la cima


della pila

Il risultato è 7

Consideriamo l’espressione 3 x 4 + 5. Ecco i primi passi della sua valutazione:

Pila operandi Pila operatori Espressione Commenti


da elaborare
Vuota V uota 3 x 4 + 5

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:

Pila operandi Pila operatori Commenti

12

Dati terminati Valuta la cima


della pila
12

17 Q uesto è il risultato

Nel valutare, invece, l’espressione 3 + 4 x 5 , inseriamo l’operatore x nella pila degli


operatori, perché prima dobbiamo leggere il suo secondo operando, dopodiché potremo
eseguire la moltiplicazione e, infine, l’addizione.

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:

Pila operandi Pila operatori Commenti

Per vedere come si gestiscono le parentesi, consideriamo l’espressione 3 x (4 + 5). Per


prima cosa la parentesi aperta viene inserita in cima alla pila degli operatori, dove viene
poi inserito anche l’operatore +. Quando incontriamo la parentesi chiusa, sappiamo di
essere pronti a valutare la cima della pila, proseguendo fino a quando non compare la
parentesi aperta corrispondente:

Pila operandi Pila operatori Espressione Commenti


da elborare
V uota Vuota 3 X (4 + 5)

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:

Se è stato letto un numero


Inseriscilo nella pila degli operandi.
Altrimenti se è stata letta una parentesi aperta
Inseriscila nella pila degli operandi.
Altrimenti se è stato letto l'operatore op
Finché l'operatore in cima alla pila ha la precedenza su op
Valuta la cima.
Inserisci op nella pila degli operatori.
Altrimenti se è stata letta una parentesi chiusa
Finché in cima alla pila non c'è una parentesi aperta
Valuta la cima.
Estrai la parentesi aperta dalla pila degli operatori.
Altrimenti se non ci sono più dati da leggere
Finché la pila degli operatori non è vuota
Valuta la cima.

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:

Impila tutti i percorsi che partono dal punto in cui ti trovi.


Finché la pila non è vuota
Estraiunpercorsodalla pila.
Segui il percorso fino a un'uscita, un incrocio o un vicolo cieco.
Se hai trovato un'uscita
Congratulazioni!
Altrimenti se hai trovato un incrocio
Impila tutti i percorsi che partono dal punto in cui ti trovi,
tranne il percorso da cui provieni.

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.

Riepilogo degli obiettivi di apprendimento


Comprendere l'architettura del Java Collections Framework
Una raccolta (collection) raggruppa insieme elementi e ne consente il recupero successivo.
Una lista (list) è una raccolta che ricorda l’ordine relativo tra i propri elementi.
Un insieme (set) è una raccolta non ordinata di elementi non duplicati.
Una mappa (map) gestisce associazioni tra chiavi e valori.
Utilizzare liste concatenate
Una lista concatenata è composta da un certo numero di nodi, ciascuno dei quali contiene
un riferimento al nodo successivo.
Aggiungere e rimuovere elementi in una data posizione di una lista concatenata è un’ope­
razione efficiente.
Visitare in sequenza gli elementi di una lista concatenata è efficiente, ma accedervi secondo
la modalità di accesso casuale non lo è.
Per accedere agli elementi presenti in una lista concatenata si usa un iteratore (o cursore).
Scegliere unlmplementazione di insieme e usarla per gestire insiemi di valori
Le classi HashSet e TreeSet implementano l’interfaccia Set.
Le implementazioni di insieme dispongono gli elementi in modo da poterli ritrovare velo­
cemente.
Si possono usare insiemi realizzati mediante tabella hash quando devono, ad esempio, contenere
oggetti di tipo String, Integer, Double, Point, Rectangle o Color.
Si possono usare insiemi realizzati mediante albero di ricerca binario con elementi di qualun­
que classe che implementi l’interfaccia Comparable, come String o Integer.
Gli insiemi non contengono elementi duplicati. L’aggiunta all’insieme di un elemento dupli­
cato di un altro elemento già presente nell’insieme viene semplicemente ignorata.
Un iteratore visita gli elementi di un insieme nell’ordine in cui questi sono memorizzati
Ja v a C ollections Fr a m e w o r k 769

all’interno dell’insieme stesso, in relazione al tipo di implementazione.


• Non si può aggiungere un elemento a un insieme usando una specifica posizione di un
iteratore.
Usare mappe per rappresentare associazioni tra chiavi e valori
• Le classi HashMap e TreeMap implementano l’interfaccia Map.
• Per trovare tutte le chiavi e i valori presenti in una mappa, si scandisce l’insieme delle chiavi
e si cerca il valore corrispondente a ciascuna chiave.
• Una funzione di hash calcola un numero intero a partire da un oggetto p.
• Una buona funzione di hash minimizza le collisioni, che avvengono quando a oggetti diversi
vengono associati codici di hash identici.
• Nelle vostre classi, sovrascrivete il metodo hashCode combinando i codici di hash delle variabili
di esemplare.
• Il metodo hashCode deve essere compatibile con il metodo equals.
Pile, code e code prioritarie
• Una pila è una raccolta di elementi con modalità di rimozione “last-in, first-out”.
• Una coda è una raccolta di elementi con modalità di rimozione “first-in, first-out”.
• Quando si estrae un elemento da una coda prioritaria viene eliminato quello avente la pri­
orità massima.
Risolvere problemi usando pile e code
• Per verificare se in un’espressione le parentesi sono accoppiate correttamente si può usare
una pila.
• Per valutare espressioni in notazione polacca inversa si può usare una pila.
• Usando due pile si possono valutare espressioni nella notazione algebrica convenzionale.
• Per poter tornare sui propri passi e prendere una strada diversa, un algoritmo di backtracking
usa una pila per ricordare le alternative ancora inesplorate.

.itsm SD JÙ li libreria pres£|)|IaIÌJM «!it» capitole^


j a v a . u t i l . Collection<E> add
add hasPrevious
contains previous
iterator set
remove java.util.Map<Kj V>
size get
java.util.HashMap<K, V> keyset
java.util.HashSet<K, V> put
java,util . Iterator<E> remove
hasNext java.Uti l . ObjectS
next hash
remove java. Uti l . PriorityOueue< E>
java.util.LinkedList<E> remove
addFirst java.util.Oueue<E>
addLast peek
getFir St java.ut il.Set<E>
getLast java.util.Stack<E>
removeFir St peek
removeLaSt pop
java.Ut il.List<E> push
IistIterator java.util.TreeMap<K, V>
java.util.ListIterator<E> java.util.TreeSet<K, V>
770 C apitolo 14

Esercìzi di riepilogo e approfondimento


Una fattura contiene una raccolta di articoli acquistati. Dovrebbe essere implementata
R 1 4 .1 .
mediante una lista o un insieme? Fornite spiegazioni.
Considerate un programma che gestisca un’agenda di appuntamenti. Dovrebbe inserirli
R 1 4 .2 .
in una lista, in una pila, in una coda o in una coda prioritaria? Fornite spiegazioni.
Una possibile implementazione di un’agenda prevede !’utilizzo di una mappa che metta
R l 4 .3 .
in corrispondenza oggetti di tipo “data”, usati come chiavi, e oggetti di tipo “evento”. Questo,
però, funziona soltanto se è previsto un unico evento per ogni possibile data. Quale altro tipo di
raccolta si può utilizzare per consentire la presenza di più eventi associati a una stessa data?
Analizzate, nella documentazione dell’interfaccia C ollection, le descrizioni dei metodi
R 1 4 .4 .
e containsAll. Descrivete come li si possa utilizzare per implementare
addAll, removeAll, retainA ll
le comuni operazioni tra insiemi (unione, intersezione, differenza e sottoinsieme).
R 1 4 .5 . Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
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 . removeFirst());
System. out. println(s t a f f . removeFirst());
System. out. println(s t a f f . removeFirst());

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

iterator = s ta ff.listI te r a to r ();


i f (iterator.next( ) . equals("Tofn")) { iterator.remove(); }
while (iterator.hasNextO ) { System .out.printIn(iterator.next()); }

* 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.

** E14.1. Scrivete un metodo


public sta tic void downsize(LinkedList<String> employeeNames, int n)

che elimini da una lista concatenata un impiegato ogni n.


E14.2. Scrivete un metodo
public sta tic void reverse(LinkedList<String> strings)

che inverta i dati presenti in una lista concatenata.


E14.3. Realizzate il crivello di Eratostene, un metodo per calcolare i numeri primi noto agli antichi
greci. Scegliete un numero n: questo metodo calcolerà tutti i numeri primi fino a n. Come prima
cosa inserite in un insieme tutti i numeri da 2 a n. Poi, cancellate tutti i multipli di 2 (eccetto 2);
vale a dire 4, 6, 8, 10, 12, ... Dopodiché, cancellate tutti i multipli di 3 (eccetto 3), cioè, 6, 9, 12,
15, ... Arrivate fino a quindi visualizzate l’insieme.
E14.4. Scrivete un programma che usi una mappa in cui sia le chiavi sia i valori sono stringhe;
rispettivamente, i nomi degli studenti e i loro voti in un esame. Chiedete all’utente del programma
se vuole inserire o rimuovere studenti, modificarne il voto o stampare tutti i voti. La visualizzazione
dovrebbe essere ordinata per nome e avere un aspetto simile a questo:
Ja v a C ollections Fr a m e w o r k 773

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

deve essere trasformata nella seguente


Lamb l i t t l e a had mary. Snow as white was fleece i t s .

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.

Al termine, visualizzate l’intero array.


Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
15
Programmazione
generica

Obiettivi del capitolo


• Capire gli obiettivi della programmazione generica
• Essere in grado di realizzare classi e metodi generici
• Comprendere il meccanismo di esecuzione di metodi generici all’interno della
macchina virtuale Java
• Conoscere le limitazioni relative alla programmazione generica in Java

La programmazione generica riguarda la progettazione e realizzazione di strutture di dati


e di algoritmi che siano in grado di funzionare con tipi di dati diversi. Conoscete già la
classe generica ArrayList, i cui esemplari possono contenere dati di qualunque tipo. In
questo capitolo imparerete a realizzare vostre classi generiche.

cap15new.indd W-1 17/06/2016 14.44.29


W-2 CAPITOLO 15

15.1 Classi generiche e tipi parametrici


La programmazione generica consiste nella creazione di costrutti di programmazione
che possano essere utilizzati con molti tipi di dati diversi. Ad esempio, i program-
matori della libreria Java che hanno realizzato la classe ArrayList hanno sfruttato le
tecniche della programmazione generica: come risultato, è possibile creare vettori che
contengano elementi di tipi diversi, come ArrayList<String>, ArrayList<BankAccount>
e così via.
Nella dichiarazione di una classe generica, occorre specificare una variabile di
Una classe generica ha uno
o più tipi parametrici.
tipo per ogni tipo parametrico. Ecco come viene dichiarata la classe ArrayList nella
libreria standard di Java, usando la variabile di tipo E per rappresentare il tipo degli
elementi:
public class ArrayList<E>
{
public ArrayList() {...}
public void add(E element) {...}
...
}

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

cap15new.indd W-2 17/06/2016 14.44.39


PROGRAMMAZIONE GENERICA W-3

15.2 Realizzare tipi generici


In questo paragrafo imparerete a realizzare vostre classi generiche. Inizieremo con una
classe generica molto semplice, che memorizza coppie di oggetti, ciascuno dei quali può
essere di tipo qualsiasi. Ad esempio:
Pair<String, Integer> result = new Pair<>("Harry Morgan", 1729);

I metodi getFirst e getSecond restituiscono il primo e il secondo valore memorizzati nella


coppia.
String name = result.getFirst();
Integer number = result.getSecond();

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:

Variabile di tipo Significato


E Tipo di un elemento in una raccolta
K Tipo di una chiave in una mappa
V Tipo di un valore in una mappa
T Tipo generico
S, U Ulteriori tipi generici

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;

public Pair(T firstElement, S secondElement)


{
first = firstElement;
second = secondElement;
}

Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore

cap15new.indd W-3 17/06/2016 14.44.39


W-4 CAPITOLO 15

public T getFirst() { return first; }


public S getSecond() { return second; }
}

Alcuni trovano più semplice partire dalla dichiarazione di una classe normale, scegliendo
tipi effettivi al posto delle variabili di tipo, come in questo esempio:

public class Pair // iniziamo con una coppia di String e Integer


{
private String first;
private Integer second;

public Pair(String firstElement, Integer secondElement)


{
first = firstElement;
second = secondElement;
}
public String getFirst() { return first; }
public Integer getSecond() { return second; }
}

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

cap15new.indd W-4 17/06/2016 14.44.39


PROGRAMMAZIONE GENERICA W-5

Sintassi di Java 15.1 Dichiarazione di una classe generica


Sintassi modalitàDiAccesso class NomeClasseGenerica<VariabileDiTipo1, VariabileDiTipo2,...>
{
variabili di esemplare
costruttori
metodi
}

Esempio Specificate una variabile per ogni tipo parametrico.


public class Pair<T, S>
{ Variabili di esemplare
private T first; di un tipo di dato parametrico.
Metodo che restituisce private S second;
un valore di tipo ...
parametrico. public T getFirst() { return first; }
...
}

/**
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

cap15new.indd W-5 17/06/2016 14.44.39


W-6 CAPITOLO 15

*/
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);
}
}

Esecuzione del programma


Diana
Expected: Diana
1
Expected: 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>?

15.3 Metodi generici


Un metodo generico è un metodo che ha un tipo parametrico e si può anche trovare in
Un metodo generico è un metodo
avente un tipo parametrico.
una classe che, per se stessa, non è generica. Potete pensare a un tale metodo come a un
insieme di metodi che differiscono tra loro soltanto per uno o più tipi di dati.Ad esempio,
potremmo voler dichiarare un metodo che possa visualizzare un array di qualsiasi tipo:

public class ArrayUtil


{
/**
Visualizza tutti gli elementi contenuti in un array.
@param a l’array da visualizzare
*/
public <T> static void print(T[] a)
{
...
}
...
}

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

cap15new.indd W-6 17/06/2016 14.44.39


PROGRAMMAZIONE GENERICA W-7

public class ArrayUtil


{
public static void print(String[] a)
{
for (String e : a)
{
System.out.print(e + " ");
}
System.out.println();
}
...
}

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

cap15new.indd W-7 17/06/2016 14.44.39


W-8 CAPITOLO 15

Sintassi di Java 15.2 Dichiarazione di un metodo generico


Sintassi modificatori <VariabileDiTipo1, VariabileDiTipo2,...> tipoRestituito nomeMetodo(parametri)
{
corpo
}

Esempio Specificate la variabile di tipo prima del tipo restituito.


public static <E> void print(E[] a)
{
for (E e : a)
Variabile locale di un tipo System.out.print(e + " ");
di dato parametrico. System.out.println();
}

15.4 Vincolare i tipi parametrici


Spesso è necessario specificare quali tipi possano essere usati in una classe generica oppure
I tipi parametrici possono essere
soggetti a vincoli.
in un metodo generico. Considerate, ad esempio, un metodo generico, min, che abbia il
compito di trovare l’elemento di valore minimo presente in un array di oggetti. Come
è possibile trovare l’elemento di valore minimo quando non si ha alcuna informazione
in merito al tipo degli elementi? Serve un meccanismo che consenta di confrontare gli
elementi dell’array. Una soluzione consiste nel richiedere che gli elementi appartengano a
un tipo che implementa l’interfaccia Comparable. In una situazione come questa, dobbiamo
quindi vincolare il tipo parametrico.
public static <E extends Comparable> E min(E[] a)
{
E smallest = a[0];
for (int i = 1; i < a.length; i++)
{
if (a[i].compareTo(smallest) < 0) { smallest = a[i]; }
}
return smallest;
}

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

cap15new.indd W-8 17/06/2016 14.44.40


PROGRAMMAZIONE GENERICA W-9

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.

Errori comuni 15.1


Genericità e ereditarietà
Se SavingsAccount è una sottoclasse di BankAccount, allora ArrayList<SavingsAccount> è una
sottoclasse di ArrayList<BankAccount>? Anche se forse ne sarete sorpresi, la risposta è no: il
legame di ereditarietà presente fra i tipi parametrici non genera un legame di ereditarietà
fra le classi generiche corrispondenti e, quindi, non esiste alcuna relazione di ereditarietà
tra ArrayList<SavingsAccount> e ArrayList<BankAccount>.
Questa limitazione è assolutamente necessaria per consentire la verifica della corri-
spondenza tra i tipi. Immaginate, infatti, che fosse possibile assegnare un oggetto di tipo
ArrayList<SavingsAccount> a una variabile di tipo ArrayList<BankAccount>, in questo modo:

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

Ma bankAccounts e savingsAccounts fanno riferimento al medesimo vettore! Se l’assegnazio-


ne indicata in grassetto fosse lecita, saremmo in grado di aggiungere un oggetto di tipo
CheckingAccount a un contenitore di tipo ArrayList<SavingsAccount>.
In molte situazioni queste limitazioni possono essere superate usando un carattere
jolly (wildcard), come descritto in Argomenti avanzati 15.1.

Argomenti avanzati 15.1


Tipi con carattere jolly (wildcard)
Spesso si ha la necessità di formulare vincoli un po’ complessi per i tipi parametrici: per
questo scopo sono stati inventati i tipi con carattere jolly (wildcard), che può essere usato
in tre diversi modi.
Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore

cap15new.indd W-9 17/06/2016 14.44.40


W-10 CAPITOLO 15

Nome Sintassi Significato


Vincolo con limite inferiore ? extends B Qualsiasi sottotipo di B
Vincolo con limite superiore ? super B Qualsiasi supertipo di B
Nessun vincolo ? Qualsiasi tipo

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:

public static void reverse(List<?> list)

Una dichiarazione di questo tipo è, in pratica, un’abbreviazione per la seguente:

public static <T> void reverse(List<T> list)

Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore

cap15new.indd W-10 17/06/2016 14.44.40


PROGRAMMAZIONE GENERICA W-11

15.5 Cancellazione dei tipi (type erasure)


Dato che i tipi generici sono stati introdotti nel linguaggio Java soltanto di recente, la
La macchina virtuale elimina i tipi
parametrici, sostituendoli con i loro
macchina virtuale che esegue programmi Java non lavora con classi o metodi generici:
vincoli o con Object. i tipi parametrici vengono “cancellati”, cioè sostituiti da tipi Java ordinari. Ciascun tipo
parametrico è sostituito dal relativo vincolo, oppure da Object se non è vincolato.
Ad esempio, la classe generica Pair<T, S> viene sostituita dalla seguente classe “grezza”
(raw):
public class Pair
{
private Object first;
private Object second;

public Pair(Object firstElement, Object secondElement)


{
first = firstElement;
second = secondElement;
}
public Object getFirst() { return first; }
public Object getSecond() { return second; }
}

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

cap15new.indd W-11 17/06/2016 14.44.40


W-12 CAPITOLO 15

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;
}
}

Analogamente, non è possibile costruire un array di un tipo generico.

public class Stack<E>


{
private E[] elements;
...
public Stack()
{
elements = new E[MAX_SIZE]; // ERRORE
}
}

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:

public class Stack<E>


{
private ArrayList<E> elements;
...
public Stack()
{
elements = new ArrayList<E>(); // così va bene
}
}

Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore

cap15new.indd W-12 17/06/2016 14.44.40


PROGRAMMAZIONE GENERICA W-13

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];
}
}

Il cast genera un avvertimento (warning) in fase di compilazione perché non è verificabile.


Queste limitazioni sono, francamente, imbarazzanti: ci auguriamo che le future ver-
sioni di Java non effettuino più la cancellazione dei tipi parametrici, in modo da poter
eliminare le attuali restrizioni che ne sono, appunto, conseguenza.

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];
}
}

Errori comuni 15.2


Usare tipi generici in un contesto statico
Non si possono usare tipi parametrici per dichiarare variabili statiche, metodi statici o
classi interne statiche. Ad esempio, quanto segue non è lecito:
public class LinkedList<E>
{
private static E defaultValue; // ERRORE
...
public static List<E> replicate(E value, int n) {...} // ERRORE

Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore

cap15new.indd W-13 17/06/2016 14.44.40


W-14 CAPITOLO 15

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
}

Riepilogo degli obiettivi di apprendimento


Classi generiche e tipi parametrici
• Una classe generica ha uno o più tipi parametrici.
• I tipi parametrici possono essere sostituiti, all’atto della creazione di esemplari, con nomi di
classi o di interfacce.
Realizzazione di classi e interfacce generiche
• Le variabili di tipo di una classe generica vanno indicate dopo il nome della classe e sono
racchiuse tra parentesi angolari.
• Per indicare i tipi generici delle variabili di esemplare, dei parametri dei metodi e dei valori
da essi restituiti, usate le variabili di tipo.
Realizzazione di metodi generici
• Un metodo generico è un metodo avente un tipo parametrico.
• I tipi parametrici di un metodo generico vanno scritti tra i modificatori e il tipo del valore
restituito dal metodo.
• Quando invocate un metodo generico, non dovete specificare esplicitamente i tipi da usare
al posto dei tipi parametrici.
Espressione di vincoli per i tipi parametrici
• I tipi parametrici possono essere soggetti a vincoli.
Limitazioni sulla programmazione generica in Java imposte dalla cancellazione dei tipi parametrici
• La macchina virtuale elimina i tipi parametrici, sostituendoli con i loro vincoli o con Object.
• Non si possono costruire oggetti o array di un tipo generico.

Esercizi di riepilogo e approfondimento


* R15.1. Cos’è un tipo parametrico?

Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore

cap15new.indd W-14 17/06/2016 14.44.40


PROGRAMMAZIONE GENERICA W-15

* 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:

public static <T extends Comparable<? super T>> void sort(List<T> a)

Perché non è sufficiente scrivere <T extends Comparable> oppure <T extends Comparable<T>>?

** R15.11. Che risultato si ottiene con la seguente verifica di condizione?

ArrayList<BankAccount> accounts = new ArrayList<BankAccount>();


if (accounts instanceof ArrayList<String>) ...

Provate e fornite una spiegazione.


*** R15.12. La classe ArrayList<E>della libreria standard di Java deve gestire un array di oggetti di
tipo E, ma, in Java, non è lecito costruire un array generico, di tipo E[]. Osservate, nel codice
sorgente della libreria, che fa parte del JDK, la soluzione che è stata adottata e fornite una
spiegazione.

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

cap15new.indd W-15 17/06/2016 14.44.40


W-16 CAPITOLO 15

*** 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.

Risposte alle domande di auto-valutazione


1. HashMap<String, Integer>

2. new Pair<String, String>("Hello", "World")

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>

7. public static <E extends Measurable> E min(E[] a)


{
E smallest = a[0];
for (int i = 1; i < a.length; i++)
{
if (a[i].getMeasure() < smallest.getMeasure())
smallest = a[i];
}
return smallest;
}

8. public static void print(Object[] a)


{
for (Object e : a)
{
System.out.print(e + " ");
}
System.out.println();
}

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

cap15new.indd W-16 17/06/2016 14.44.40


A
Il sottoinsieme Basic Latin
di Unicode

Tabella 1 C a r a tte r e C o d ic e D e c im a le C a r a tte r e C o d ic e D e c im a le


I sottoinsieme Basic Latin I '\U 0021' 33 6 ' \ u0036' 54
(ASCII) di Unicode “ '\U 0022' 34 7 '\u 0 0 3 7 ' 55
# '\U 0023' 35 8 ' \ u0038' 56
$ '\U0024' 36 9 '\u 0 0 3 9 ' 57
% '\U 0025' 37 ' \ u003A' 58
& '\U 0026' 38 J ' \ u003B' 59
‘ '\u 0 0 2 7 ' 39 < '\u003C ' 60
( '\U 0028' 40 = ' \ u003D' 61
) '\u 0 0 2 9 ' 41 > ' \ u003E' 62
★ '\u002A ' 42 ? ' \ u003F' 63
'\U002B' 43 @ ' \u0040' 64
'\U002C' 44 A '\u 0 0 4 l' 65
- '\U002D' 45 13 ' \u 0042' 66
'\u002E ' 46 C ’\u0043' 67
/ '\U002F' 47 D '\u0044' 68
0 '\u 0 0 3 0 ' 48 E '\u0045' 69
1 '\U 0031' 49 F ' \ u0046' 70
2 '\U 0032' 50 G '\u0047' 71
3 '\U 0033' 51 H ' \ u0048' 72
4 '\u 0 0 3 4 ' 52 I '\u0049' 73
'\u004A'
5 '\u 0 0 3 5 ' 53 J 74
(cotitimui)
778 A p p e n d ic e A

( 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

Operatore Descrizione Associati vita


Accesso alle caratteristiche di una classe da sinistra a destra
[] Indice di array da sinistra a destra
0 Invocazione di metodo da sinistra a destra
++ Incremento da destra a sinistra
— Decremento da destra a sinistra
! Not booleano da destra a sinistra
Not bit a bit da destra a sinistra
+ (unario) (nessun effetto) da destra a sinistra
- (unario) Inversione di segno da destra a sinistra
(NomeTipo) Cast da destra a sinistra
new Creazione di oggetto da destra a sinistra
♦ Moltiplicazione da sinistra a destra
/ Divisione o divisione intera da sinistra a destra
% Resto della divisione intera da sinistra a destra

( continua)
780 A p p e n d ic e B

( seguito)

Operatore Descrizione Associatività


+ Addizione o concatenazione di stringhe da sinistra a destra
- Sottrazione da sinistra a destra
<< Scorrimento a sinistra da sinistra a destra
» Scorrimento a destra con estensione del segno da sinistra a destra
»> Scorrimento a destra con estensione di zeri da sinistra a destra
< Minore di da sinistra a destra
<= Minore di o uguale a da sinistra a destra
> Maggiore di da sinistra a destra
>= Maggiore di o uguale a da sinistra a destra
instanceof Verifica se un oggetto è di un certo tipo
O di un suo sottotipo da sinistra a destra
== Uguale da sinistra a destra
!= Diverso da sinistra a destra
8i And bit a bit da sinistra a destra
Or esclusivo bit a bit da sinistra a destra
I Or bit a bit da sinistra a destra
&& And booleano con “cortocircuito” da sinistra a destra
II Or booleano con “cortocircuito” da sinistra a destra
?: Condizionale da destra a sinistra
= Assegnamento da destra a sinistra
operatore= Assegnamento con operatore binario
(operatore può essere +, -, *, /, &, |, <<, >>, >>>) da destra a sinistra
C
Linguaggio Java:
parole riservate

Parola riservata Descrizione


abstract Una classe o un metodo astratti
assert Un’asserzione di una condizione che deve essere verificata
boolean Il tipo booleano
break Interrompe l’attuale ciclo o enunciato con etichetta
byte Il tipo intero a 8 bit
case Un’etichetta in un enunciato switch
catch Il gestore di un’eccezione in un blocco try
char Il tipo carattere Unicode a 16 bit
class Definiscecuna classe
const Non usata
continue Ignora la parte restante del corpo di un ciclo
default L’etichetta predefinita in un enunciato switch o un metodo predefinito
do Un ciclo il cui corpo viene eseguito almeno una volta
double Il tipo in virgola mobile con doppia precisione a 64 bit
else La clausola alternativa in un enunciato if
enum Un tipo enumerativo
extends Indica che una classe è una sottoclasse di un’altra
final Un valore che non può essere modificato dopo essere stato inizializzato,
un metodo che non può essere ridefmito oppure una classe che non può
essere estesa
finally Una clausola di un blocco try che viene sempre eseguita

( 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

1729decimale = 1 X IO^ + 7 X 1Q2 + 2 X IQl + 9 X 10^^

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

1101bi„3„o-l X23 + X 22 + 0 X 2' + 1 X 2<> = 8 + 4 + 1 = 13

Per le cifre che seguono il punto “decimale”, si usano le potenze negative di 2.

1•^Olbmario = 1 X 2^>+ 1 X 2"i + 0 X 2"2 + 1 X = 1 + 1/2 + 1/8 = 1 + 0.5 + 0.125 = 1.625

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

Per convertire in binario un numero intero decimale, lo si divide ripetutamente per 2,


tenendo traccia dei resti e fermandosi quando il dividendo diventa 0. Poi, si scrivono i
resti come numero binario, iniziando d 2i\Vultimo. A d esempio:

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

Quindi, lOOdedmale = 11001 OObinario-


Per convertire in binario, invece, un numero frazionario minore di 1, lo si moltiplica
ripetutamente per 2:se il risultato è maggiore di l,si sottrae l;se il numero da moltiplicare
diventa 0, la conversione è terminata. Poi, si scrivono le cifre che precedono il punto
decimale come cifre binarie della parte frazionaria, iniziando dalla prima. A d esempio:

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

A questo punto lo schema si ripete, quindi la rappresentazione binaria di 0.35 è 0.01


011001100110 ...

Per convertire in binario un numero decimale generico, convertite la parte intera e


la parte frazionaria separatamente.
S is t e m i d i n u m e r a z io n e 785

Errori di arrotondamento e overflow


In Java, un valore di tipo int è un numero intero che occupa 32 bit. Eseguendo operazioni
tra due valori di questo tipo, è possibile che il risultato non trovi posto in 32 bit: una
situazione che prende il nome di overflow (“trabocco”). In tal caso vengono usati solamente
gli ultimi 32 bit del risultato, dando luogo a un valore errato. Ad esempio, il frammento
di codice seguente visualizza 705032704:

int fiftyMillion = 50000000;


System.out.printIn(100 * fiftyMillion); // Previsto: 5000000000

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:

double price = 4.35;


double quantity = 100;
double total = price * quantity; // dovrebbe essere 100 * 4.35 = 435
System.out.println(total); // visualizza 434.99999999999999

Per capire come si possa verificare questo errore, eseguiamo anche in questo caso la
moltiplicazione in colonna:

1 1 0 0 1 0 0 * 1 0 0.0 i | o 1 1 o|o 1 1 o|o 1 1 0 . . .

1 0 0.0 l | 0 1 1 o|o 1 1 o|o 1 1 0 . . .


1 0 0.0 l | 0 1 1 o|o 1 1 o|o 1 1 0 . . .
0
0
1 0 0.0 l | 0 1 1 0|0 1 1 0|0 1 1 0 . . .
0
0
786 A p p e n d ic e D

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:

-13 = 10001101, e segno

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:

• Cambiate il valore di tutti i bit.


• Quindi, aggiungete 1.

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.

iIsnslaillS iillS U Ilim sri in virgola mobile


Lo standard IEEE-754 (IEEE, Institute for Electrical and Electronics Engineering)
definisce le rappresentazioni per i numeri in virgola mobile. La Figura I mostra come
i valori in singola precisione (float) e in doppia precisione (double) siano composti da:

□ un bit di segno
□ un esponente
□ una mantissa

1 numeri in virgola mobile usano la notazione scientifica, nella quale un numero viene
rappresentato come

In questa rappresentazione, e è l’esponente, mentre le cifre • compongono la


mantissa. La rappresentazione n o r m a liz z a ta è quella avente ^ 0. Per esempio:

^^^decimale ~ ~ ^ ^

Poiché il primo bit di una rappresentazione normalizzata deve necessariamente essere 1,


questo in realtà nel sistema di numerazione binario non viene memorizzato nella mantissa,
per cui dovete sempre aggiungerlo per ottenere il valore vero. Ad esempio, la mantissa
1.100100 viene memorizzata come 100100.
La parte della rappresentazione IEEE riservata all’esponente non usa né la
rappresentazione in complemento a due né quella in modulo e segno: viene aggiunto
all’esponente vero una quantità fissa, detta /)/45.Tale quantità è 127 per i numeri in singola
precisione e 1023 per quelli in doppia precisione. Ad esempio, l’esponente e = 6 verrebbe
memorizzato come 133 in un numero in singola precisione.
Quindi:

,
100decimale = 0 110 0 0 0 10 11 10010000000000000000000 IEEE singola precisione

Ci sono, poi, alcuni valori speciali. Fra questi:

□ Zero: esponente con bias = 0, mantissa = 0.


□ Infinito: esponente con bias = 11... 1, mantissa = ±0.
□ N aN {not a number, non è un numero valido): esponente con bias = 1 1 ... 1, mantissa ^ ±0.
788 A p p e n d ic e D

Figura 1 1 bit 8 bit 23 bit


R a p p re s e n ta zio n e IEEE
Mantissa
p er n u m e ri Segno Esponente con bias
e + 127 (senza 1 iniziale)
in v irg o la m o b ile
Singola precisione
1 bit 11 bit 52 bit
Esponente con bias Mantissa
Segno (senza 1 iniziale)
e + 1023

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

11 11011IOOOl binario 3B1

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

Scorrimento a destra con estensione del segno ( » )

\ \ \ \ . \ V

W W W

0 0 \

Scorrimento a destra con estensione di zeri ( » > )

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

Per controllare se il bit n-esimo di un numero vale 1, eseguite la verifica seguente:

if ((x &1 << n) != 0) . . .

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

Numeri di Fibonacci La sequenza di numeri 1, 1, 2, 3, Ordinamento per selezione Un algoritmo di ordina­


5, 8, 13, ...,in cui ogni elemento è la somma dei suoi mento che cerca e rimuove ripetutamente l’elemento
due predecessori. di valore minimo, finché non rimangono più elementi.
Numeri pseudocasuali Una sequenza di numeri che sem­ Ordinamento quicksort Un algoritmo di ordinamento,
brano essere casuali ma sono, in realtà, generati mediante solitamente veloce, che sceglie un elemento (denominato
una formula matematica. “pivot”), partiziona la sequenza da ordinare in due parti
Numero in virgola mobile Un numero che può avere contenenti, rispettivamente, gli elementi minori e gli
una parte frazionaria. elementi maggiori del pivot, quindi ordina ricorsivamente
Numero intero Un numero che non può avere una parte tali sotto-sequenze.
frazionaria. Pacchetto (package) Un insieme di classi tra loro correlate.
Numero letterale Un valore fisso all’interno di un pro­ Per accedere a una o più classi contenute in un pacchetto
gramma che viene scritto esplicitamente sotto forma di si usa l’enunciato import.
numero, come - 2 o 6.02214115E23. Pacchetto di prove (test suite) Un insieme di casi di prova
Numero magico Un numero che compare in un pro­ per un programma.
gramma senza alcuna spiegazione. Pannello Un componente dell’interfaccia grafica privo
O ggetto valore il cui tipo è una classe. di aspetti visibili, solitamente utilizzato per raggruppare
Oggetto anonimo Un oggetto che non viene memoriz­ altri componenti.
zato in una variabile. Pannello dei contenuti In Swing, quella porzione di
Oggetto semplificato (mock) Un oggetto che viene frame che contiene i componenti dell’interfaccia utente.
utilizzato durante il collaudo di un programma per sosti­ Parametro esplicito In un metodo, un parametro diverso
tuire un altro oggetto avente un comportamento simile; dall’oggetto con cui il metodo è stato invocato.
solitamente l’oggetto mock è di più semplice realizzazione Parametro implicito L’oggetto con cui viene invocato un
O particolarmente progettato per agevolare il collaudo. metodo. Ad esempio, nell’invocazione x .f(y ),l’oggetto x
Operatore Un simbolo che rappresenta un’operazione è il parametro implicito del metodo f .
matematica o logica, come + o &&. Parola riservata Una parola che ha uno speciale significato
Operatore binario Un operatore che richiede due argo­ in un linguaggio di programmazione e che, quindi, non
menti O operandi, come l’operatore + nell’espressione può essere utilizzata come nome dai programmatori.
X + y. Passaggio dei parametri L’azione di definizione di
Operatore booleano o logico Un operatore che può espressioni che fungano da argomenti di un metodo nel
essere applicato a variabili booleane. Java dispone di tre momento in cui questo viene invocato.
operatori booleani: &&, | j e !. Percorso (di un file o cartella) La sequenza di nomi di
Operatore new Un operatore che crea nuovi oggetti. cartelle che descrive come raggiungere un file o una
Operatore relazionale Un operatore che confronta due cartella a partire da una determinata posizione nel file
valori fornendo un risultato di tipo booleano. system.
Operatore ternario Un operatore dotato di tre operandi. Permutazione Una disposizione degli elementi apparte­
Java ha un solo operatore ternario, a ? b : c. nenti a un insieme di valori.
Operatore unario Un operatore che ha un solo operando. Pila Una struttura di memorizzazione da cui gh elementi
Ordinamento lessicografico L’ordinamento di strin­ vengono estratti con una strategia “l’ultimo entrato è il
ghe in modo simile a quanto avviene in un dizionario, primo a uscire”. Gli elementi possono essere aggiunti
ignorando tutte le coppie di caratteri corrispondenti ed eliminati in una sola posizione, denominata “cima”
che siano identici e confrontando i primi caratteri di della pila.
ciascuna stringa che differiscono tra loro. Ad esempio, Pila delle invocazioni (cali stack) L’insieme ordinato di
nell’ordinamento lessicografico “orbit” precede “orchid”. metodi che, in un certo istante, sono stati invocati ma
Notate che in Java, diversamente da quanto avviene in non hanno ancora terminato la propria esecuzione; in
un dizionario, l’ordinamento è sensibile alla differenza cima alla pila c’è il metodo attualmente in esecuzione,
tra maiuscole e minuscole: Z precede a. mentre in fondo c’è il metodo main.
Ordinamento per fusione Un algoritmo di ordinamento Pila di esecuzione (run-tim e stack) La struttura che
che ordina due metà di una struttura di dati, per poi memorizza le variabili locali di tutti i metodi invocati
fonderle insieme. durante l’esecuzione di un programma.
G l o s s a r io 797

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

.Riepìlogo degli obiettivi dì appi;§Qdìn\i^p|9


Ereditarietà, superclasse e sottoclasse
Una sottoclasse eredita dati e comportamenti da una superclasse.
Un oggetto di una sottoclasse può sempre essere utilizzato al posto di un oggetto della sua
superclasse.
Implementare sottoclassi
Una sottoclasse eredita tutti i metodi che non sovrascrive.
Una sottoclasse può sovrascrivere un metodo ereditato dalla sua superclasse fornendone
una nuova implementazione.
La parola riservata extends definisce la relazione di ereditarietà.
Una sottoclasse può sovrascrivere metodi della sua superclasse
Un metodo che sovrascrive !’omonimo metodo della superclasse ne può estendere o
sostituire la funzionalità.
Per invocare un metodo della superclasse si usa la parola riservata super.
Un costruttore di una sottoclasse invoca il costruttore della superclasse senza parametri,
a meno che non ci sia una diversa indicazione esplicita.
Per invocare un costruttore della superclasse in un costruttore di una sottoclasse si usa la
parola riservata super come primo enunciato.
Un costruttore di una sottoclasse può fornire argomenti a un costruttore della superclasse
usando la parola riservata super.
Uso del polimorfismo per elaborare oggetti di tipi correlati
Un riferimento a una sottoclasse può essere utilizzato in ogni punto del programma che
preveda la presenza di un riferimento alla sua superclasse.
Quando la macchina virtuale invoca un metodo di esemplare, usa il metodo della classe a cui
appartiene il parametro implicito: si parla di “ricerca dinamica del metodo”.
11polimorfismo (“avere molte forme”) ci consente di manipolare oggetti che hanno in comune
alcune funzionalità, anche se queste sono implementate in modi diversi.
La classe object e i suoi metodi
Sovrascrivete il metodo toString in modo che restituisca una stringa che descrive lo stato
dell’oggetto.
11 metodo equals verifica se due oggetti hanno lo stesso contenuto.
Se sapete che un oggetto è un esemplare di una determinata classe, potete usare un cast
per convertirlo in quel tipo.
L’operatore instanceof verifica se un oggetto è di un determinato tipo.

J^^rcizi di riepilooo e approfondimento ...........


R9.1. Nella sezione Esempi completi 9.1:
a. quali so n o le sottoclassi di Employee?
b. quali so n o le superclass! di Manager?
c. quali so n o le superclass! e le sottoclassi di SalariedEmployee?
d. quali classi sovrascrivono il m e to d o weeklyPay della classe Employee?
Ereditarietà 509

e. quali classi sovrascrivono il metodo setName della classe Employee?


f. quali sono le variabili di esemplare di un oggetto di tipo HourlyEmployee?
R9.2. Nelle seguenti coppie di classi, individuate la superclasse e la sottoclasse:
a. Employee, Manager (c io è dipendente e dirigente)
b. CraduateStudent, Student {studente universitario e studente)
c. Person, Student (persona e studente)
d. Employee, Professor (dipendente e professore)
e. BankAccount, CheckingAccount (conto bancario e conto corrente bancario)
f. Vehiele, Car (veicolo e automobile)
g. Vehiele, Minivan (veicolo e automobilefamiliare)
h. Car, Minivan (automobile e automobilefamiliare)
i. Truek, Vehiele (autocarro e veicolo)

R9.3. In un programma che gestisce l’inventario in un negozio di piccoli elettrodomestici,


perché non è utile definire la superclasse SmallApplianee (piccolo elettrodomestico) con le sottoclassi
Toaster (tostapane), CarVaeuum (aspirapolvere per automobile), TravelIron (ferro da stiro da viaggio) e
così via?
R9.4. Quali metodi eredita dalla propria superclasse la classe ChoieeQuestion? Quali metodi
sovrascrive? Quali metodi aggiunge?
R9.5. Quali metodi eredita dalla propria superclasse la classe SavingsAeeount vista nella sezione
Consigli pratici 9.1? Quali metodi sovrascrive? Quali metodi aggiunge?
R9.6. Elencate le variabili di esemplare di un oggetto di tipo CheekingAeeount, classe definita
nella sezione Consigli pratici 9.1.
R9.7. Se la classe Sub estende la classe Sandwieh, quali fra le seguenti sono assegnazioni permesse
dopo aver eseguito i primi due enunciati?
Sandwich x = new Sandwich();
Sub y = new Sub();

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.10. Quali relazioni di ereditarietà stabilireste fra le classi seguenti?


Student (studente)
Professor (professore)
TeachingAssistant (assistente del professore)
Employee (dipendente)
Secretary (segretario)
DepartmentChair (direttore di dipartimento)
Janitor (bidello)
SeminarSpeaker (oratore di seminario)
Person (persona)
Course (corso)
Seminar (seminario)
Lecture (lezione)
ComputerLab (esercitazione in laboratorio)

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.

a. System.out instanceof PrintStream


b. System.out instanceof OutputStream
c. System.out instanceof LogStream
d. System.out instanceof Object
e. System.out instanceof String
f. System.out instanceof Writer

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ì:

The inventor of Dava was ___

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.

E9.16. Risolvete nuovamente l’esercizio precedente memorizzando la posizione del punto


in un oggetto di tipo java.aw t.Point. Il metodo toString deve invocare il metodo toString della
classe Point.
E9.17 (economia). Modificate la classe CheckingAccount vista nella sezione Consigli pratici
9.1 in modo che prelevi una commissione di $1 per ogni versamento o prelievo eseguito
oltre le tre transazioni mensili gratuite. Inserite il codice che calcola la commissione dovuta
in un metodo separato, che verrà invocato dai metodi deposit e withdraw.

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

quali sono i valori di args[o], args[l] e così via?


R l 1.6. Qual è la differenza tra lanciare e catturare un’eccezione?
R l 1.7. Cos’è un’eccezione a controllo obbligatorio? Cos’è un’eccezione a controllo non obbli­
gatorio? Fate un esempio di entrambe le categorie. Quali eccezioni siete obbligati a dichiarare
con una clausola throws?
R l 1.8. Perché non è obbligatorio dichiarare che un metodo può lanciare un’eccezione IndexOu-
tOfBoundsException?

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!

il programma deve scrivere il file:


/* 1 */ Mary had a little lamb
/* 2 */ Whose fleece was white as snow.
/* 3 */ And everywhere that Mary went,
/* 4 */ The lamb was sure to go!
624 C a p it o l o 11

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

report.txt: has broken up an international ring of DVD bootleggers that


address.txt: Kris Kringle, North Pole
address.txt: Homer Simpson, Springfield
Homework, java: String filename;

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

il contenuto di HelloPrinter.java diventa


retnirPolleH ssalc cilbup
{
)sgra ][gnirtS(niam diov citats cilbup
{
;)"!dlroW ,olleH"(nltnirp.tuo.metsyS
}
}
Naturalmente eseguendo due volte Reverse sullo stesso file si ottiene di nuovo il file originale.
El 1.11. Scrivete un programma che legga un file, una riga per volta, e ne scriva le righe in un
altro file in ordine inverso. Se, ad esempio, il file input.txt ha questo contenuto:
Mary had a little lamb
Whose fleece was white as snow
And everywhere that Mary went
The lamb was sure to go.
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 625

e il programma viene eseguito in questo modo:


java ReverseFile input.txt output.txt

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

Riepìlogo degli obiettivi dì apprendimento


Comprendere il flusso d'esecuzione di un'elaborazione ricorsiva
• U n ’e la b o ra z io n e ricorsiva riso lv e u n p ro b lem a u san d o la s o lu z io n e del p rob lem a stesso nel
caso di dati di in gresso p iù sem p lici.
• P erch é una r ic o r s io n e te rm in i, d e v o n o esistere casi sp eciali p er i dati in in gresso p iù sem p lici.

Individuare i metodi ausiliari ricorsivi utili per risolvere un problema


• A v o lte è p iù se m p lice trovare una so lu z io n e ricorsiva d o p o aver a p p ortato una p ic co la m o ­
d ifica al p rob lem a o rig in a r io .

Confrontare l'efficienza di algoritmi ricorsivi e non ricorsivi


• A v o lte un a s o lu z io n e ricorsiva v ie n e eseg u ita m o lto più len ta m en te di una s o lu z io n e itera­
tiva del m e d e sim o p rob lem a, m a nella m a g g io r parte d ei casi la s o lu z io n e ricorsiva è so lta n to
p o c o più len ta.
• In m o lti casi una s o lu z io n e ricorsiva è p iù fa cile da capire e da realizzare co rr etta m e n te , ri­
sp etto a un a so lu z io n e iterativa.

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 .

La ricorsione mutua: l'esempio di un valutatore di espressione


• U n a r ic o r s io n e m u tu a è caratterizzata da un in sie m e di m e to d i c o o p e r a n ti c h e si in v o c a n o
l ’un l’altro r ip etu ta m en te.

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.

Esercizi di riepilogo e approfondimento


* R 1 2 . 1 . D e fin ite i se g u e n ti term in i:
a. r ic o rsio n e
b. itera zio n e
c. r ic o r s io n e in fin ita
d. m e to d o au siliario rico rsiv o

** R 1 2 .2 . D e lin e a te , senza im p lem en ta rla , una so lu z io n e ricorsiva p er trovare il valore m in im o in un


array.

k-k-k R 1 2 .3 . D e lin e a te , senza im p lem en ta rla , un a s o lu z io n e ricorsiva p er trovare il fe-esim o e le m e n to


più p ic c o lo in u n array. S u j^ e r im e n to : cercate gli e le m e n ti c h e so n o m in o r i d e ll’e le m e n to iniziale;
se q u esti so n o m , c o m e si p r o c ed e se k ^ m ? E s e k > m ?

** R 1 2 .4 . D e lin e a te , senza im p lem en ta rla , una s o lu z io n e ricorsiva p er ordinare un array di n u m eri.


S u ii^ e rim e n to : p er prim a cosa trovate il valore m in im o n e ll’array.*

* R 1 2 . 5 . D e lin e a te , senza im p lem en ta rla , un a so lu z io n e ricorsiva p er ordinare un array di n u m eri.


i n s e r i m e n t o ’, p er prim a cosa ord in ate il so tto-array p rivo d e ll’e le m e n to in iziale.
R ic o r s io n e 675

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 . 7 . N e l caso in cu i ri sia pari, m ig lio ra te l ’ese rc iz io p r e ce d e n te c a lc o la n d o x ” c o m e


P erch é qu esta so lu z io n e è d e c isa m en te p iù v e lo c e? S u g g e r im e n to : ca lco la te ^ ^^io24 en tram b i
i m o d i.

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 0 . N e l r o m p ica p o d e lle “ T orri di H a n o i” v isto nella s e z io n e E sem p i c o m p le ti 1 2 .2 in d i­


c h ia m o c o n m o sse(« ) il n u m er o di m o sse n ecessarie p er spostare n d isch i.T rovate una form u la c h e
esp rim a m osse(H ) in fu n z io n e di m osse(iJ — 1), p o i d im ostrate c h e m osse(n ) = 2" — 1.

R 1 2 . i l . D e lin e a te , senza im p lem en ta rla , un a s o lu z io n e ricorsiva p er gen erare tutti i so tto in s ie m i


d e ll’in sie m e { 1 , 2 , ..., n } .

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.

R l 2 .1 3 . S e g u ite passo d o p o passo l ’e s e c u z io n e del p rogram m a di v a lu ta zio n e d elle esp ression i,


v isto nel Paragrafo 1 2 .5 , q u a n d o analizza 3 - 4 + 5, 3 - (4 + 5), (3 - 4) * 5 e 3 * 4 + 5 * 6.

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 . 3 . S criv ete un m e to d o rico rsiv o c h e sc o m p o n g a in fattori p rim i u n n u m er o in tero n. Per


prim a cosa trovate un f a t t o r e ,/ p o i sc o m p o n e te r ic o rsiv a m en te in fattori n / f .

^ 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 . 5 . S criv ete u n m e to d o r ico rsiv o String reverse(String text) c h e inverta il c o n te n u to di una


stringa. A d e se m p io , reverse("Hello! ") restitu isce la stringa "!olleH". R e a liz za te una so lu z io n e ri­
corsiva e lim in a n d o il p r im o carattere, in v e rte n d o la stringa r im a n en te e c o m b in a n d o le d u e parti.

** E 1 2 .6 . R is o lv e te di n u o v o l ’e sercizio p r e ce d e n te usan d o, p erò , u n m e to d o au siliario rico rsiv o c h e


in v erte una sottostrin ga.

* E 1 2 . 7 . R e a liz za te itera tivam en te il m e to d o reverse d e ll’E se r d z io E l 2 .5 .

** 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

public static boolean find(String text, String str)

c h e v erifica se la stringa te x t c o n tie n e la stringa str. A d e se m p io , find("Mississippi”, ”sip") resti­


tu isce true. S u ^ e r i m e n t o : se il testo in izia c o n la stringa c h e state cerca n d o , avete fin ito ; a ltrim en ti,
co n sid era te la stringa c h e si o ttie n e e lim in a n d o il p r im o carattere.

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

public static int indexOf(String text, String str)

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.

E 1 2 .1 0 . U s a n d o la r ic o rsio n e, trovate l ’e le m e n to m assim o in un array. S u g g e r im e n to : trovate l ’e le ­


m e n to m assim o n el s o tto in s ie m e c h e c o n tie n e tutti gli e le m e n ti d e ll’array tranne l ’u ltim o ; q u in d i,
c o n fro n ta te tale m a ssim o c o n il valore d e ll’u ltim o e le m e n to .

E 1 2 . i l . U s a n d o la r ic o rsio n e, ca lco la te la so m m a di tutti i valori presen ti in un array.

E 1 2 .1 2 . U sa n d o la r ic o rsio n e, ca lco la te l ’area di u n p o lig o n o . Id en tific a ten e una p o r z io n e trian ­


golare, c o m e in d ic a to nella figura, e sfruttate il fatto c h e u n tr ia n g o lo c o n v ertici (x j, y i), (x 2, y2)
e f e » Y i) ha area:

\x\y2 + X 2 Y3 + - nx2 - 72^3 - y3^l|


2

E 1 2 .1 3 . Il m e to d o se g u e n te p er il c a lc o lo della rad ice quadrata era n o t o a n c h e agli a n tic h i g r ec i.


D a to u n v alore x > 0 e u n valore g c a n d id a to a essere la rad ice quadrata di x , il va lo re (g + x / g ) /
2 è u n ca n d id a to m ig lio r e d i S c r i v e t e u n m e to d o au silia rio r ic o rsiv o , squareRootGuess(double x,
double g): S e ^ è circa u g u a le a x r estitu is c e ^ , a ltrim en ti r estitu isce il valore r estitu ito dal m e to d o
squareRootGuess in v o c a to c o n il c a n d id a to m ig lio r a to u sa n d o la fo r m u la d escritta in p r e ce d e n z a .
S c r iv e te, p o i, il m e to d o public static squareRoot(double x) c h e usi tale m e to d o au siliario.

E 1 2 .1 4 . R e a liz za te una classe SubstringGenerator c h e g e n e r i tu tte le so tto strin g h e di una stringa.


A d e se m p io , le so tto strin g h e della stringa "rum” s o n o q u este sette strin gh e:

rum , um ,
R ic o r s io n e 677

S u g g e r im e n to : d ap prim a trovate tu tte le so tto strin g h e c h e in iz ia n o c o n il p r im o carattere, c h e so n o


n se la strin ga ha lu n g h e zz a n; p o i, trovate le so tto str in g h e della stringa c h e si o ttie n e e lim in a n d o
il p r im o carattere.

E 1 2 . 1 5 . R e a liz z a te una classe SubsetGenerator c h e g e n e r i tu tti i so tto in s ie m i d ei caratteri di una


stringa. A d e se m p io , i so tto in s ie m i di caratteri della stringa "rum" so n o q u este o tto strin gh e:

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".

E 1 2 .1 6 . G en erate r ico rsiv a m en te tutti i m o d i in cu i u n vetto re p u ò essere su d d iv iso in una se q u e n ­


za di s o tto -v e tto r i n o n v u o ti. A d e se m p io , d ato il vetto re [1, 7 , 2, 9 ], d o v e te restituire il se g u e n te
v etto re di vettori:

[[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 . 1 7 . D a to un vetto re a di n u m er i in teri, trovate ric o rsiv a m e n te tutti i v etto ri di e le m e n ti di a


la cu i so m m a è u g u a le al n u m er o in tero «.

E 1 2 . 1 8 . Im m a g in a te di v o ler salire una scala di n grad in i (id en tifica ti da n u m er i naturali in se­


q u en za) e di p o te r fare u n g ra d in o o d u e a o g n i passo. E len ca te ric o rsiv a m e n te tu tte le possib ili
se q u e n z e di grad in i. A d e se m p io , se n vale 5, le se q u e n z e p o ssib ili son o:

[1, 2, 3, 4, 5], [1, 3, 4, 5], [l, 2, 4, 5], [l, 2, 3, 5], [l, 4, 5]

E 1 2 . 1 9 . R is o lv e te n u o v a m en te l ’ese rc iz io p r e ce d e n te n e ll’ip o te si c h e a o g n i passo si p ossa n o salire


fin o a k grad in i.

E 1 2 .2 0 . D a to u n p r e zz o so tto fo r m a di n u m e r o in tero , e le n c a te u sa n d o la r ic o r s io n e tu tti i


m o d i p o ssib ili p er pagarlo c o n b a n c o n o te da $ 1 0 0 , $ 2 0 , $ 5 e $ 1 , sen za c h e sia n o p resen ti m o d i
r ip etu ti.

E 1 2 . 2 1 . M ig lio ra te il valu tatore di esp ression i del Paragrafo 1 2 .5 in m o d o c h e possa gestire l ’o p e ­


ratore %e l ’o p eratore di “ e le v a m e n to a p o te n z a ” ''.A d e se m p io , 2 '' 3 vale 8. C o m e in m atem atica,
l ’e le v a m e n to a p o te n z a d e v e avere la p rece d e n z a sulla m o ltip lic a zio n e : 5 * 2 '' 3 vale 40 .

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.

E 1 2 .2 3 . M ig lio ra te il p rogram m a c h e riso lv e il ro m p ica p o d e lle o tto r eg in e in m o d o c h e n o n


v isu a lizzi s o lu z io n i c h e sian o ro ta zio n i o riflession i di so lu z io n i visu alizzate in p reced en za . 11 p ro­
gra m m a d e v e visualizzare d o d ic i s o lu z io n i.

E 1 2 .2 4 . M ig lio ra te il p rogram m a c h e risolve il ro m p ica p o d e lle o tto r eg in e in m o d o c h e le s o lu ­


z io n i v e n g a n o scritte in u n file H T M L , u san d o tab elle c o n sfo n d o b ia n co e n ero p er le sca cch iere
e il carattere U n ic o d e "\u2655' p er le regin e.
678 C a p it o l o 12

E l 2 .2 5 . R e n d e t e più g e n era le il p rogram m a c h e risolve il r o m p ica p o d elle o tto r eg in e in m o d o


c h e risolva il p rob lem a p er n reg in e. 11 p rogram m a d ev e c h ied er e all’u te n te il valore di n e, p o i,
visualizzare le so lu z io n i

E 1 2 . 2 6 . U s a n d o il b ack track in g, scrivete u n p rogram m a c h e risolva ro m p icap i rappresentati da


a d d izio n i tra strin g h e di lettere, d o v e o g n i lettera d e v e essere sostitu ita da un a cifra, c o m e questi:

send + more = money


base + ball = games
kyoto + Osaka = tokyo

E 1 2 . 2 7 . 11 c a lc o lo rico rsiv o d ei n u m er i di F ib o n a cci p u ò essere a ccelerato in m o d o sig n ifica tiv o


te n e n d o traccia d ei valori c h e s o n o già stati calcolati: realizzate una n u ova v e rsio n e del m e to d o
fib u san d o questa strategia. O g n i volta c h e restitu ite u n n u o v o valore, m e m o r iz z a te lo an ch e in un
array ausiliario; p rim a di iniziare il c a lc o lo di un valore, co n su lta te !’array p er v ed ere se tale c a lc o lo
sia già stato e se g u ito . C o n fro n ta te il te m p o di e s e c u z io n e della v e rsio n e co sì m igliorata c o n le
realizzazion i o r ig in a li, ricorsiva e iterativa.

Sul sito Web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
O rdinamento e ricerca 723

Tempi di esecuzione degli algoritm i di ricerca lineare e di ricerca binaria


• La ricerca lineare esamina tutti i valori di un array fino a trovare una corrispondenza con
quanto cercato o la fine dell’array.
• La ricerca lineare trova un valore in un array eseguendo un numero di passi 0{tt).
• La ricerca binaria cerca un valore in un array ordinato determinando se si può trovare nella
prima o nella seconda metà dell’array, ripetendo poi la ricerca in una delle due metà.
• La ricerca binaria trova la posizione di un valore in un array eseguendo un numero di passi
0(log(»i)).
Stime dell'andamento O grande delle prestazioni di algoritm i
• Un ciclo che esegue n iterazioni, ciascuna delle quali è costituita da un numero fisso di azioni,
richiede un tempo 0{n).
• Un ciclo che esegue n iterazioni, ciascuna delle quali richiede un tempo 0(/;), è 0{tr).
• La stima O-grande del tempo necessario per eseguire più fasi in sequenza è pari al valore di
O-grande avente crescita più rapida tra quelli relativi alle singole fasi.
• Un ciclo che esegue n iterazioni, la /-esima delle quali richiede un tempo 0(i), è 0{n~).
• Un algoritmo che a ogni passo divide a metà il problema viene eseguito in un tempo 0(log(«)).
Utilizzo dei metodi della libreria di Java per ordinare dati e fare ricerche
• La classe Arrays contiene il metodo di ordinamento che dovrebbe essere normalmente uti­
lizzato nei programmi Java.
• La classe Collections contiene un metodo di ordinamento che agisce su vettori.
• 11 metodo sort della classe Arrays ordina oggetti di classi che implementano l’interfaccia
Comparable.

Elgjiienti dì lìbrgri)|jre;$entatì in questo capìtolo


java.lang.System java.util.Collections
currentTimeMillis binarySearch
java.util.Arrays sort
binarySearch java .util. Campara t o K T>
sort compare
comparing

Esercizi di riepiiogo e^pr«#lMliinen|;d ,


* R13.1. Qual è la differenza fra cercare e ordinare?
R13.2. Attenzione al rischio di errori per scarto di uno. Scrivendo l’algoritmo di ordinamento per
selezione visto nel Paragrafo 13.1, occorre, come al solito, scegliere tra < e <=, tra a.length e
a.length - 1, e tra from e from + i: un terreno fertile per la proliferazione degli errori per scarto di
uno. Eseguite passo dopo passo il codice dell’algoritmo applicato ad array di lunghezza 0,1,2 e 3,
controllando accuratamente che tutti i valori degli indici siano corretti.
R13.3. Qual è l’andamento di crescita, in termini di O-grande, di queste funzioni?
a. + 2 /1 + 1
b. n10 + 9ri^ + IOn^ + \45rP
c. (n + 1)^
d. ( / r + //)“
724 C apitolo 13

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)

e, posto J{n) = rt-y confrontateli con


/(2000) / / ( 1000)
/4000)//(1000)
/ 10000) / / 1000)

* 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.

0(«) o(«=) 0(n>) 0{ti log tt) 0(2")


1000 5 5 5 5 5
2000
3000 45
10000

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.

R 1 3 . i l . Considerate il problema di trovare l’elemento più frequente in un array di lunghezza


n. Ecco tre possibili approcci:

a. Ordinare !’array, poi trovare la serie più lunga.


b Creare un array di contatori avente la stessa dimensione delTarray originario. Per ciascun
elemento, scandire l’intero array contando il numero di elementi uguali a quello in esame,
aggiornando il relativo contatore. Quindi, trovare il contatore che ha il valore massimo.
C Usare variabili per memorizzare l’elemento visto più frequentemente finora e la sua frequenza.
Per ogni indice i, verificare se a[i] è presente nelle posizioni a[o] . . . a[i - i]. Se non è
presente,contare quante volte è presente in a [i + l] . . . a [ n - i ] . S e a [ i ] ricorre più volte
dell’elemento visto più frequentemente finora, aggiornare le variabili.
Descrivete !’efficienza dei tre approcci in termini di O-grande.

R 1 3 . 1 2 . Seguite, passo dopo passo, l’esecuzione dell’algoritmo di ordinamento per selezione


applicato a questi due array:

a. 4, 7, 11, 4, 9, 5, 11, 7, 3, 5
b. - 7 , 6, 8, 7, 5, 9, 0, 11, 10, 5, 8

R 1 3 . 1 3 . Seguite,passo dopo passo, l’esecuzione dell’algoritmo di ordinamento per fusione applicato


a questi due array:

a. 5, 11, 7, 3, 5, 4, 7, 11, 4, 9
b. 9, 0, 11, 10, 5, 8, -7, 6, 8, 7, 5

R13.14. Seguite, passo dopo passo, l’esecuzione dei seguenti algoritmi:

a. Ricerca lineare di 7 in -7, i, 3, 3, 4, 7, i i , 13


b. Ricerca binaria di 8 in -7, 2, 2, 3, 4, 7, 8, i l , 13

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

allora deve essere modificato in modo da contenere


4 7 11 9 5 3

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.

Qual è la sua efficienza, in termini di O-grande?


R13.22. L’algoritmo di ordinamento per radice {radix sort) ordina un array di n numeri interi, cia­
scuno avente d cifre in formato decimale, usando dieci array ausiliari associati agli indici da 0 a 9.
Per prima cosa, inserisce ciascun valore u dell’array nell’array ausiliario il cui indice corrisponde
all’ultima cifra di v, quindi trasferisce nuovamente tutti gli elementi nell’array originale, preser­
vando il loro ordine (cioè prima trasferisce gli elementi dall’array ausiliario 0, poi quelli dall’array
ausiliario 1, e così via). Quindi la procedura viene ripetuta usando la penultima cifra di ciascun
valore come indice per decidere !’array ausiliario di destinazione, e così via. Qual è l’efficienza di
questo algoritmo in termini di O-grande, in funzione di n e di d? In quali casi questo algoritmo
è da preferirsi rispetto all’ordinamento per fusione?
** R13.23. Un algoritmo di ordinamento stabile {stable sort) non modifica l’ordine relativo di elementi
che hanno lo stesso valore, una caratteristica utile e richiesta in molte applicazioni. Considerate,
O rdinamento e ricerca 727

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]

E13.12. Scrivete un programma che ordini un esemplare di ArrayList<Country> in ordine decrescente,


in modo che la nazione di superficie maggiore si trovi all’inizio del vettore. Usate un Comparator.
E13.13. Considerate l’algoritmo di ricerca binaria del Paragrafo 13.6: se non trova l’elemento
cercato, il metodo search restituisce -1 . Modificatelo in modo che, se !’elemento non viene trovato,
venga invece restituito il valore -/e —1, dove ^ è la posizione in cui l’elemento dovrebbe essere
inserito (che è il comportamento di Arrays.binarySearch).
E13.14. Realizzate senza ricorsione il metodo sort dell’algoritmo di ordinamento per fusione,
nell’ipotesi che la lunghezza delTarray sia una potenza di 2. Prima fondete le regioni adiacenti di
dimensione 1, poi le regioni adiacenti di dimensione 2, quindi le regioni adiacenti di dimensione
4 e così via.
E13.15. Usate l’ordinamento per inserimento e la ricerca binaria dell’Esercizio E13.13 per ordinare
un array secondo quanto descritto nell’Esercizio R 13.20. Realizzate tale algoritmo e misuratene
le prestazioni.
E13.16. Scrivete una classe Person che implementi l’interfaccia Comparable, confrontando persone
in base al loro nome. Chiedete all’utente di inserire dieci nomi e generate dieci oggetti di tipo
Person. Usando il metodo compareTo, determinate la prima e l’ultima persona dell’insieme e
visualizzatele.
E13.17. Ordinate un vettore di stringhe in ordine crescente di lunghezza. Su^erimento: progettate
un apposito Comparator.
E13.18. Ordinate un vettore di stringhe in ordine crescente di lunghezza, in modo che stringhe
della stessa lunghezza vengano disposte in ordine lessicografico. Suggerimento: progettate un apposito
Comparator.

Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
770 C apitolo 14

Esercìzi di riepilogo e approfondimento


Una fattura contiene una raccolta di articoli acquistati. Dovrebbe essere implementata
R 1 4 .1 .
mediante una lista o un insieme? Fornite spiegazioni.
Considerate un programma che gestisca un’agenda di appuntamenti. Dovrebbe inserirli
R 1 4 .2 .
in una lista, in una pila, in una coda o in una coda prioritaria? Fornite spiegazioni.
Una possibile implementazione di un’agenda prevede !’utilizzo di una mappa che metta
R l 4 .3 .
in corrispondenza oggetti di tipo “data”, usati come chiavi, e oggetti di tipo “evento”. Questo,
però, funziona soltanto se è previsto un unico evento per ogni possibile data. Quale altro tipo di
raccolta si può utilizzare per consentire la presenza di più eventi associati a una stessa data?
Analizzate, nella documentazione dell’interfaccia C ollection, le descrizioni dei metodi
R 1 4 .4 .
e containsAll. Descrivete come li si possa utilizzare per implementare
addAll, removeAll, retainA ll
le comuni operazioni tra insiemi (unione, intersezione, differenza e sottoinsieme).
R 1 4 .5 . Spiegate che cosa viene visualizzato dal codice seguente.Tracciate uno schema della lista
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 . removeFirst());
System. out. println(s t a f f . removeFirst());
System. out. println(s t a f f . removeFirst());

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

iterator = s ta ff.listI te r a to r ();


i f (iterator.next( ) . equals("Tofn")) { iterator.remove(); }
while (iterator.hasNextO ) { System .out.printIn(iterator.next()); }

* 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.

** E14.1. Scrivete un metodo


public sta tic void downsize(LinkedList<String> employeeNames, int n)

che elimini da una lista concatenata un impiegato ogni n.


E14.2. Scrivete un metodo
public sta tic void reverse(LinkedList<String> strings)

che inverta i dati presenti in una lista concatenata.


E14.3. Realizzate il crivello di Eratostene, un metodo per calcolare i numeri primi noto agli antichi
greci. Scegliete un numero n: questo metodo calcolerà tutti i numeri primi fino a n. Come prima
cosa inserite in un insieme tutti i numeri da 2 a n. Poi, cancellate tutti i multipli di 2 (eccetto 2);
vale a dire 4, 6, 8, 10, 12, ... Dopodiché, cancellate tutti i multipli di 3 (eccetto 3), cioè, 6, 9, 12,
15, ... Arrivate fino a quindi visualizzate l’insieme.
E14.4. Scrivete un programma che usi una mappa in cui sia le chiavi sia i valori sono stringhe;
rispettivamente, i nomi degli studenti e i loro voti in un esame. Chiedete all’utente del programma
se vuole inserire o rimuovere studenti, modificarne il voto o stampare tutti i voti. La visualizzazione
dovrebbe essere ordinata per nome e avere un aspetto simile a questo:
Ja v a C ollections Fr a m e w o r k 773

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

deve essere trasformata nella seguente


Lamb l i t t l e a had mary. Snow as white was fleece i t s .

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.

Al termine, visualizzate l’intero array.


Sul sito web dedicato al libro si trova una raccolta di progetti di programmazione più complessi.
W-14 CAPITOLO 15

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
}

Riepilogo degli obiettivi di apprendimento


Classi generiche e tipi parametrici
• Una classe generica ha uno o più tipi parametrici.
• I tipi parametrici possono essere sostituiti, all’atto della creazione di esemplari, con nomi di
classi o di interfacce.
Realizzazione di classi e interfacce generiche
• Le variabili di tipo di una classe generica vanno indicate dopo il nome della classe e sono
racchiuse tra parentesi angolari.
• Per indicare i tipi generici delle variabili di esemplare, dei parametri dei metodi e dei valori
da essi restituiti, usate le variabili di tipo.
Realizzazione di metodi generici
• Un metodo generico è un metodo avente un tipo parametrico.
• I tipi parametrici di un metodo generico vanno scritti tra i modificatori e il tipo del valore
restituito dal metodo.
• Quando invocate un metodo generico, non dovete specificare esplicitamente i tipi da usare
al posto dei tipi parametrici.
Espressione di vincoli per i tipi parametrici
• I tipi parametrici possono essere soggetti a vincoli.
Limitazioni sulla programmazione generica in Java imposte dalla cancellazione dei tipi parametrici
• La macchina virtuale elimina i tipi parametrici, sostituendoli con i loro vincoli o con Object.
• Non si possono costruire oggetti o array di un tipo generico.

Esercizi di riepilogo e approfondimento


* R15.1. Cos’è un tipo parametrico?

Cay Horstmann: Concetti di informatica e fondamenti di Java 6a ed. - Copyright 2016 Maggioli editore

cap15new.indd W-14 17/06/2016 14.44.40


PROGRAMMAZIONE GENERICA W-15

* 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:

public static <T extends Comparable<? super T>> void sort(List<T> a)

Perché non è sufficiente scrivere <T extends Comparable> oppure <T extends Comparable<T>>?

** R15.11. Che risultato si ottiene con la seguente verifica di condizione?

ArrayList<BankAccount> accounts = new ArrayList<BankAccount>();


if (accounts instanceof ArrayList<String>) ...

Provate e fornite una spiegazione.


*** R15.12. La classe ArrayList<E>della libreria standard di Java deve gestire un array di oggetti di
tipo E, ma, in Java, non è lecito costruire un array generico, di tipo E[]. Osservate, nel codice
sorgente della libreria, che fa parte del JDK, la soluzione che è stata adottata e fornite una
spiegazione.

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

cap15new.indd W-15 17/06/2016 14.44.40


W-16 CAPITOLO 15

*** 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.

Risposte alle domande di auto-valutazione


1. HashMap<String, Integer>

2. new Pair<String, String>("Hello", "World")

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>

7. public static <E extends Measurable> E min(E[] a)


{
E smallest = a[0];
for (int i = 1; i < a.length; i++)
{
if (a[i].getMeasure() < smallest.getMeasure())
smallest = a[i];
}
return smallest;
}

8. public static void print(Object[] a)


{
for (Object e : a)
{
System.out.print(e + " ");
}
System.out.println();
}

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

cap15new.indd W-16 17/06/2016 14.44.40

Potrebbero piacerti anche