Relazione tra metodo hashCode e equals in Java

Ho letto in molti punti mentre dicevo che override equals method in Java, dovrebbe sovrascrivere anche il metodo hashCode , altrimenti “sta violando il contratto”.

Ma finora non ho riscontrato alcun problema se ho eseguito l’override del metodo solo uguale, ma non del metodo hashCode.

Qual è il contratto? E perché non sto affrontando alcun problema quando sto violando il contratto? In tal caso dovrò affrontare un problema se non ho sovrascritto il metodo hashCode?

Il problema che si presenta è con le raccolte in cui l’unicità degli elementi viene calcasting in base a .equals() e .hashCode() , ad esempio le chiavi in ​​una HashMap .

Come suggerisce il nome, si basa sulle tabelle hash e i bucket hash sono una funzione dell’object .hashCode() .

Se hai due oggetti che sono .equals() , ma hanno codici hash diversi, perdi!

La parte del contratto qui che è importante è: gli oggetti che sono .equals() DEVONO avere lo stesso .hashCode() .

Questo è tutto documentato in javadoc per Object . E Joshua Bloch dice che devi farlo in Java efficace . È stato detto abbastanza.

Secondo il documento, l’implementazione predefinita di hashCode restituirà alcuni numeri interi che differiscono per ogni object

Per quanto ragionevolmente pratico, il metodo hashCode definito dalla class Object restituisce interi distinti per oggetti distinti. (Questo in genere viene implementato convertendo l’indirizzo interno dell’object in un numero intero, ma questa implementazione
la tecnica non è richiesta dal linguaggio di programmazione JavaTM.)

Tuttavia, qualche volta si desidera che il codice hash sia lo stesso per oggetti diversi che hanno lo stesso significato. Per esempio

 Student s1 = new Student("John", 18); Student s2 = new Student("John", 18); s1.hashCode() != s2.hashCode(); // With the default implementation of hashCode 

Questo tipo di problema si verificherà se si utilizza una struttura di dati hash nel framework di raccolta come HashTable, HashSet. Specialmente con la raccolta come HashSet finirai per avere elementi duplicati e violare il contratto Set.

Sì, dovrebbe essere ignorato. Se ritieni di dover eseguire l’override di equals() , devi eseguire l’override di hashCode() e viceversa. Il contratto generale di hashCode () è:

  1. Ogni volta che viene invocato sullo stesso object più di una volta durante l’esecuzione di un’applicazione Java, il metodo hashCode deve restituire costantemente lo stesso numero intero, a condizione che non vengano modificate le informazioni utilizzate nei confronti degli uguali sull’object. Questo numero intero non deve rimanere coerente da un’esecuzione di un’applicazione a un’altra esecuzione della stessa applicazione.

  2. Se due oggetti sono uguali secondo il metodo equals (Object), quindi chiamare il metodo hashCode su ciascuno dei due oggetti deve produrre lo stesso risultato intero.

  3. Non è necessario che se due oggetti non sono uguali secondo il metodo equals (java.lang.Object), allora chiamare il metodo hashCode su ciascuno dei due oggetti deve produrre risultati interi distinti. Tuttavia, il programmatore dovrebbe essere consapevole del fatto che la produzione di risultati interi distinti per oggetti non uguali può migliorare le prestazioni degli hashtables.

Vedi JavaDoc di java.lang.Object

In hashCode() dice:

Se due oggetti sono uguali secondo il metodo equals(Object) , quindi chiamare il metodo hashCode su ciascuno dei due oggetti deve produrre lo stesso risultato intero .

(Enfasi da parte mia).

Se si esegue l’override di equals() e non di hashCode() la class viola questo contratto.

Questo è anche detto nel metodo JavaDoc del metodo equals() :

Si noti che è generalmente necessario sovrascrivere il metodo hashCode ogni volta che questo metodo viene sovrascritto, in modo da mantenere il contratto generale per il metodo hashCode , che stabilisce che gli oggetti uguali devono avere uguali codici hash.

Un contratto è: se due oggetti sono uguali, allora dovrebbero avere lo stesso codice hash e se due oggetti non sono uguali possono o meno avere lo stesso codice hash.

Prova a usare il tuo object come chiave in HashMap (modificato dopo i commenti di joachim-sauer), e inizierai ad avere problemi. Un contratto è una linea guida, non qualcosa forzato su di te.

Il contratto è che se obj1.equals(obj2) then obj1.hashCode() == obj2.hashCode() , è principalmente per motivi di prestazioni, in quanto le mappe utilizzano principalmente il metodo hashCode per confrontare le chiavi delle voci.

Hashmaps un’occhiata a Hashtables , Hashmaps , HashSets e così via. Tutti memorizzano la chiave hash come loro chiavi. Quando si richiama get(Object key) viene generato l’hash del parametro e si cerca negli hash specificati.

Quando non si sovrascrive hashCode() e l’istanza della chiave è stata modificata (ad esempio una stringa semplice che non ha alcuna importanza), l’ hashCode() potrebbe generare 2 diversi hashcode per lo stesso object, risultando non trovare il proprio dato chiave in map.get() .