Confronti, equals - UniCam · Laboratorio di Programmazione - Luca Tesei 10 Confronto di Stringhe...

32
1 Laboratorio di Programmazione - Luca Tesei Confronti, equals Operatori Relazionali, confronti Confronti fra Oggetti, ridefinizione di equals

Transcript of Confronti, equals - UniCam · Laboratorio di Programmazione - Luca Tesei 10 Confronto di Stringhe...

1Laboratorio di Programmazione - Luca Tesei

Confronti, equals

Operatori Relazionali, confronti

Confronti fra Oggetti, ridefinizione di equals

2Laboratorio di Programmazione - Luca Tesei

Operatori relazionali

● All’interno delle parentesi tonde della condizione dell’if è possibile, come abbiamo visto, inserire il confronto tra due valori poiché questa espressione ha un valore di tipo boolean (true o false)

● Vediamo, nella tabella seguente, tutti gli operatori relazionali che possiamo usare:

3Laboratorio di Programmazione - Luca Tesei

Operatori Relazionali

Diverso!=

Uguale===

Minore o uguale<=

Minore<<

Maggiore o uguale>=

Maggiore>>

DescrizioneNotazione matematicaOperatore Java

4Laboratorio di Programmazione - Luca Tesei

Errore comune: = invece di ==

● I programmatori ancora inesperti spesso confondono l’uso di = con ==

● In particolare un errore comune è quello di inserire in un confronto l’operatore = invece che ==

• if (a=5) b++; // Errore di compilazione● L’espressione a=5, oltre ad essere un

assegnamento e non un confronto, ha un valore di tipo int

5Laboratorio di Programmazione - Luca Tesei

Confronti di valori in virgola mobile

● Gli operatori == o != non hanno molto senso applicati a valori in virgola mobile

● Un errore tipico è quello di cercare di controllare se un valore double (o float) sia uguale a 0.0

● È molto improbabile che un double risultante da un calcolo sia esattamente zero

● Potrebbe essere invece un valore prossimo allo zero

6Laboratorio di Programmazione - Luca Tesei

Confronti di valori in virgola mobile

● Prendiamo ad esempio questo semplice programma:

double r = Math.sqrt(2);double d = r * r -2;if (d == 0) System.out.println("Radice quadrata di 2 per 2 meno 2 fa 0");else System.out.println("Radice quadrata di 2 per 2 meno 2 fa " + d);

● Il valore stampato non è zero: d vale 4.440892098500626E-16 che è prossimo a zero, ma non è zero!

7Laboratorio di Programmazione - Luca Tesei

Confronti di valori in virgola mobile

● In generale, per confrontare l’uguaglianza di due valori in virgola mobile è bene fissare una soglia EPSILON di tolleranza e vedere se i due valori sono sufficientemente prossimi rispetto a questa

● Per esempio si può definirefinal double EPSILON = 1E-14;● E poi controllare se |x-y| EPSILON. Se è vero

si può decidere che i due valori vanno considerati “uguali”

8Laboratorio di Programmazione - Luca Tesei

Confronti di valori in virgola mobile

● Tuttavia, se x e y sono valori molto grandi, la loro differenza potrebbe essere una quantità maggiore di EPSILON, ma comunque, vista la loro grandezza, essere irrisoria

● È bene, quando x e y non sono prossimi a zero, considerare la differenza dei due valori rapportata alla loro grandezza:

|)||,max(|

||

yx

yx

9Laboratorio di Programmazione - Luca Tesei

Confronti di valori in virgola mobile

● In codice java:final double EPSILON = 1E-14;double x, y;...if (Math.abs(x-y) / Math.max(Math.abs(x), Math.abs(y)) <= EPSILON)

System.out.println(“\”Uguali\””);else System.out.println(“\”Diversi\””);

10Laboratorio di Programmazione - Luca Tesei

Confronto di Stringhe

● Le stringhe in Java, lo sappiamo, sono oggetti● Quindi il valore di una variabile di frame di tipo String

non è la stringa in sé, ma un riferimento all’oggetto stringa

● Se cercassimo di confrontare direttamente due variabili di tipo stringa staremmo semplicemente controllando se puntano allo stesso oggetto

● Ma in generale ciò che ci interessa è sapere se il contenuto di due stringhe (oggetti stringa diversi) è lo stesso

11Laboratorio di Programmazione - Luca Tesei

Confronto di Stringhe

...String pippo = console.readLine();String pluto = console.readline();if (pippo == pluto) //falso System.out.println(“Stringhe Uguali”)else System.out,println(“Stringhe Diverse”)● Per il confronto del contenuto di due stringhe dobbiamo

usare il metodo equals della classe String● Come tipico della programmazione ad oggetti, un

operatore binario fra due oggetti viene realizzato con un metodo che va chiamato su uno dei due e a cui va passato un riferimento all’altro oggetto operando

12Laboratorio di Programmazione - Luca Tesei

Confronto di Stringhe

System.out.println("Inserisci una stringa");String pippo = console.readLine();System.out.println("Inserisci una seconda stringa per il confronto");String pluto = console.readLine();if ( pippo == pluto ) System.out.println("Confronto con == : Stringhe Uguali");else System.out.println("Confronto con == : Stringhe Diverse");if ( pippo.equals(pluto) ) System.out.println("Confronto con metodo equals: Stringhe” + “

Uguali");else System.out.println("Confronto con metodo equals: Stringhe” + “

Diverse");

● Inserendo due stringhe uguali il primo confronto fallisce, mentre il secondo ha successo

13Laboratorio di Programmazione - Luca Tesei

Confronto di Stringhe

● La classe String fornisce anche operatori di confronto “corrispondenti” a <, >

● Il metodo compareTo ha un parametro String in ingresso e confronta la stringa su cui è chiamato con la stringa passata come parametro

● Il valore di uscita è un int il cui valore indica se la stringa passata è uguale, “maggiore” o “minore” nel senso di ordine alfabetico

14Laboratorio di Programmazione - Luca Tesei

Confronto di Stringhe

int r = string1.compareTo(string2);● Se r > 0 allora string1 precede string2

nell’ordine alfabetico (lessicografico)● Se r < 0 allora string1 segue string2

nell’ordine alfabetico (lessicografico)● Se r == 0 allora le stringhe sono uguali

(alternativa a equals)

15Laboratorio di Programmazione - Luca Tesei

Confronto di oggetti

● Il discorso fatto per le stringhe si applica agli oggetti in generale

● Applicando l’operatore == a due variabili riferimento si controlla semplicemente se puntano allo stesso oggetto

● Se si vuole invece confrontare lo stato si deve fornire la classe di un metodo equals simile a quello che viene fornito con String

16Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

● Se guardiamo il codice della classe String vediamo che il metodo equals è così definito:public boolean equals(Object anObject) {if (this == anObject) { return true;}if (anObject instanceof String) { String anotherString = (String) anObject; ......}return false;}

Esegue il controllo carattereper carattere, se le stringhe sono

uguali ritorna true, altrimenti ritorna false

Sono esattamente lo stesso oggetto

17Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

● La classe Object definisce un metodo equals che identifica due oggetti se sono lo stesso, cioè se i riferimenti puntano alla stessa area di memoria heap

● Ogni classe eredita questo metodo equals

● Ogni classe dovrebbe ridefinirlo nello stesso modo in cui viene fatto nella classe String

● Vediamo in dettaglio...

18Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

● Il metodo deve prendere come parametro un generico Object:

public boolean equals(Object anObject) {

● Per prima cosa controlla se il riferimento all'oggetto passato è uguale a quello di questo oggetto

● Se è così sicuramente anche bisogna rispondere true (è ciò che fa equals di Object)

● Se non è così allora bisogna controllare il tipo dell'oggetto passato

19Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

● L'operatore instanceof restituisce true se un riferimento generico a Object punta in realtà a un certo oggetto di una classe data:

if (anObject instanceof String) {● Se questo non è vero allora bisognerà

rispondere false (due oggetti di classi diverse in genere non vengono identificati)

● Se invece è questo il caso si dovranno controllare le variabili istanza per decidere l'uguaglianza...

20Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

● La prima cosa da fare è un casting per riottenere un riferimento alla classe vera:

String anotherString = (String) anObject;

● Poi si confronteranno i valori degli oggetti this e anotherString per decidere se si considerano questi due oggetti uguali

● Non bisogna comunque dimenticare che anche se equals ridefinito identifica due oggetti diversi questi rimangono sempre oggetti distinti!

21Laboratorio di Programmazione - Luca Tesei

Codice hash

● Se si ridefinisce il metodo equals è sempre bene ridefinire anche il metodo hashCode della classe Object

● Tale metodo restituisce un intero associato all'oggetto

● Questo valore viene utilizzato come chiave per inserire l'oggetto in una tabella hash

● Non ci addentriamo nei particolari di questa struttura dati

22Laboratorio di Programmazione - Luca Tesei

Codice hash

● Per i nostri scopi ci basta sapere che deve valere la seguente proprietà:

● Se due oggetti sono uguali allora devono avere lo stesso codice hash

● Il viceversa non è richiesto● Se ridefiniamo il metodo equals dobbiamo

quindi ridefinire anche hashCode per rispettare questa proprietà

23Laboratorio di Programmazione - Luca Tesei

Codice hash

● Un modo per ridefinire il metodo è di scegliere un numero primo (va bene 31 o 37...)

● Per ogni variabile istanza che viene usata nella definizione di equals si considera il relativo codice hash

● Per questo:

– Le classi involucro di int, double, etc forniscono il relativo metodo hashCode

– String e le altre classi delle API che ridefiniscono equals forniscono un adeguato hashCode

24Laboratorio di Programmazione - Luca Tesei

Codice hash

● Supponiamo ad esempio che var1, var2, var3 siano i nomi delle tre variabili istanza che vengono usate per definire equals

● Il codice hash dell'oggetto va allora calcolato così:int hash = 1;

hash = hash * 31 + var1.hashCode();

hash = hash * 31 + var2.hashCode();

hash = hash * 31 + var3.hashCode();

return hash;

25Laboratorio di Programmazione - Luca Tesei

Codice hash

● Questo algoritmo va utilizzato soltanto se ci sono almeno due variabili istanza che definiscono l'uguaglianza

● Se l'uguaglianza è definita solo in base a una certa variabile istanza si può utilizzare semplicemente l'hashCode di questa

26Laboratorio di Programmazione - Luca Tesei

Esempio

● Consideriamo la classe Persona che rappresenta una persona

public class Persona{

private String cognome;

private String nome;

private String luogoDiNascita;

private int annoDiNascita, meseDiNascita, giornoDiNascita;

private double altezza, peso;

...

27Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

● Supponiamo per ora che una persona sia distinta dall'altra da cognome, nome, luogo di nascita e data di nascita

● Allora è bene ridefinire il metodo equals basandosi su queste informazioni

● Spesso le informazioni che servono per ridefinire equals hanno un valore che resta immutato per tutta la vita dell'oggetto

● E' bene quindi definirle final

28Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

public class Persona{

private final String cognome;

private final String nome;

private final String luogoDiNascita;

private final int annoDiNascita, meseDiNascita, giornoDiNascita;

private double altezza, peso;

private String coloreCapelli, coloreOcchi, segniParticolari;

.........

29Laboratorio di Programmazione - Luca Tesei

Ridefinizione di equals

public boolean equals (Object other){

Persona o = null;

if (this == other) return true;

if (other instanceof Persona)

o = (Persona) other;

else return false;

return this.cognome.equals(o.cognome) && this.nome.equals(o.nome) && this.luogoDiNascita.equals(o.luogoDiNascita) && this.annoDiNascita == o.annoDiNascita && this.meseDiNascita == o.meseDiNascita && this.giornoDiNascita == o.giornoDiNascita; }

AND Logico

30Laboratorio di Programmazione - Luca Tesei

Ridefinizione di hashcode

● Ci troviamo nella situazione di più variabili● I tipi sono int e String

● Dobbiamo usare i metodi hashcode della classe Integer e della classe String

● Scegliamo il 31 come numero primo di base del calcolo:

31Laboratorio di Programmazione - Luca Tesei

Ridefinizione di hashcode

public int hashcode(){

int hash = 1 + 31 * cognome.hashcode();

hash = hash + 31 * nome.hashcode();

hash = hash + 31 * luogoDiNascita.hashcode();

Integer x = new Integer(annoDiNascita);

hash = hash + 31 * x.hashcode();

x = new Integer(meseDiNascita);

hash = hash + 31 * x.hashcode();

x = new Integer(giornoDiNascita);

hash = hash + 31 * x.hashcode(); return hash; }

32Laboratorio di Programmazione - Luca Tesei

Esercizio

● Aggiungere la variabile istanza String codiceFiscale e usarla come unico valore di identificazione dell'oggetto

● Ridefinire equals e hashcode di conseguenza