Equals e hashCode

I metodi equals() ed hashCode() sono metodi particolari di java che ogni oggetto eredita dalla classe java.lang.Object. Molto spesso è utile ridefinire il metodo equals() al fine di mettere a fattor comune il codice di confronto tra due oggetti, ma quando viene fatto deve essere ridefinito concordemente anche il metodo hashCode(). In questo articolo cerchiamo di spiegarne brevemente le motivazioni.

Metodo Equals

Questo particolare metodo è usato per eseguire il confronto uguale tra due oggetti. In generale esistono due tipi di confronti in Java, uno utilizzando l’operatore == e l’altro utilizzando equals().  Il primo esegue un confronto per riferimento, ovvero i due oggetti sono uguali se si riferiscono alla stessa area di memoria. Il metodo equals() invece intende realizzare una relazione di equivalenza tra oggetti. Se però si esamina il codice sorgente nella classe Object, si troverà il seguente codice:

Ciò implica che di fatto nell’implementazione di default i due operatori hanno lo stesso comportamento, ovvero in entrambe i casi i due oggetti sono uguali se puntano alla stessa area di memoria.

Metodo HashCode

Tale metodo fornisce un codice hash dell’oggetto ed è pensato per fornire supporto alla gestione delle strutture dati di tipo hash table come ad esempio java.util.Hashtable. La sua implementazione di default sostanzialmente non fa altro che mappare l’indirizzo dell’area di memoria dove l’oggetto è allocato con un intero (univoco).

Perché Ridefinirli Entrambi

Il motivo per cui ridefinire solamente uno dei due metodi non è sufficiente è ben spiegato nel javadoc del metodo hashCode() in cui è descritto il comportamento che tale metodo deve rispettare. Comportamento che può essere riassunto nei seguenti tre punti:

  1. Se invocato sullo stesso oggetto più di una volta durante un’esecuzione di un’applicazione Java, il metodo hashCode() deve restituire lo stesso valore intero, a condizione che non vengano modificate le informazioni utilizzate nel metodo equals(). Non è necessario che tale valore intero rimanga coerente da un’esecuzione di un’applicazione a un’altra esecuzione della stessa applicazione.
  2. Se due oggetti sono uguali secondo il metodo equals() invocare il metodo hashCode() su ciascuno dei due oggetti deve produrre lo stesso risultato intero.
  3. Non è necessario che il metodo hashCode() produca risultati distinti quando invocato su oggetti che risultino non uguali secondo il metodo equals(). Tuttavia restituire hash code distinti per oggetti non uguali può migliorare le prestazioni degli hash table.

Esempio

Supponiamo di avere una classe Customer la cui implementazione è la seguente:

Se non ridefiniamo i metodi equals() ed hashCode() il comportamento rispetta quanto stabilito nel contratto al paragrafo precedente e quindi il seguente codice main() genererà un output coerente.

In domini reali però un cliente con medesimo nome e codice fiscale rappresenta la stessa persona fisica. Sovrascriviamo quindi il metodo equals() nel seguente modo:

Eseguendo il main() definito sopra l’output diviene:

Questa volta il contratto non viene rispettato perchè sebbene i due oggetti risultino eguali, coerentemente col metodo equals(), l’hash code associato è differente (punto 2 del contratto). Ridefiniamo quindi in modo coerente il metodo hashCode(), utilizzando una metodologia classica che si basa sull’utilizzo di numeri primi scelti al fine di limitare al massimo le collisioni:

Questa volta l’output del main() ritorna ad essere coerente con il contratto:

JDK 7

Dalla versione 7 in su del JDK è disponibile una nuova classe java.util.Objects che espone metodi utili alla stesura coerente dei metodi equals() ed hashCode():

Codice Sorgente

Il codice sorgente con gli esempi presentati è scaricabile qui equals-hashcode.

© 2018 Java Boss - Theme by HappyThemes