Confronti, equals
Transcript
Confronti, equals
Confronti, equals Operatori Relazionali, confronti Confronti fra Oggetti, ridefinizione di equals Laboratorio di Programmazione - Luca Tesei 1 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: Laboratorio di Programmazione - Luca Tesei 2 Operatori Relazionali Operatore Java Notazione matematica Descrizione > > Maggiore >= Maggiore o uguale < < Minore <= Minore o uguale == = Uguale != Diverso Laboratorio di Programmazione - Luca Tesei 3 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 ● Laboratorio di Programmazione - Luca Tesei 4 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 Laboratorio di Programmazione - Luca Tesei 5 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! Laboratorio di Programmazione - Luca Tesei 6 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ò definire final double EPSILON = 1E-14; ● E poi controllare se |x-y| EPSILON. Se è vero si può decidere che i due valori vanno considerati “uguali” ● Laboratorio di Programmazione - Luca Tesei 7 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: | x y| max(| x |, | y |) Laboratorio di Programmazione - Luca Tesei 8 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\””); ● Laboratorio di Programmazione - Luca Tesei 9 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 Laboratorio di Programmazione - Luca Tesei 10 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 Laboratorio di Programmazione - Luca Tesei 11 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 Laboratorio di Programmazione - Luca Tesei 12 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 Laboratorio di Programmazione - Luca Tesei 13 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) Laboratorio di Programmazione - Luca Tesei 14 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 Laboratorio di Programmazione - Luca Tesei 15 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; Sono esattamente lo stesso oggetto } if (anObject instanceof String) { String anotherString = (String) anObject; ...... Esegue il controllo carattere } per carattere, se le stringhe sono return false; uguali ritorna true, altrimenti } ritorna false Laboratorio di Programmazione - Luca Tesei 16 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... Laboratorio di Programmazione - Luca Tesei 17 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 Laboratorio di Programmazione - Luca Tesei 18 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... Laboratorio di Programmazione - Luca Tesei 19 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! Laboratorio di Programmazione - Luca Tesei 20 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 Laboratorio di Programmazione - Luca Tesei 21 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à Laboratorio di Programmazione - Luca Tesei 22 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 Laboratorio di Programmazione - Luca Tesei 23 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; Laboratorio di Programmazione - Luca Tesei 24 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 Laboratorio di Programmazione - Luca Tesei 25 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; ... Laboratorio di Programmazione - Luca Tesei 26 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 Laboratorio di Programmazione - Luca Tesei 27 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; ......... Laboratorio di Programmazione - Luca Tesei 28 Ridefinizione di equals public boolean equals (Object other){ Persona o = null; if (this == other) return true; if (other instanceof Persona) AND Logico 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; } Laboratorio di Programmazione - Luca Tesei 29 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: Laboratorio di Programmazione - Luca Tesei 30 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; } Laboratorio di Programmazione - Luca Tesei 31 Esercizio ● ● Aggiungere la variabile istanza String codiceFiscale e usarla come unico valore di identificazione dell'oggetto Ridefinire equals e hashcode di conseguenza Laboratorio di Programmazione - Luca Tesei 32