2010-04-25 15 views
26

Ok, ho sentito da molti posti e fonti che ogni volta che sovrascrivo il metodo equals(), ho bisogno di sovrascrivere anche il metodo hashCode(). Ma si consideri il seguente frammento di codicePerché dovrei eseguire l'override di hashCode() quando sovrascrivo il metodo equals()?

package test; 

public class MyCustomObject { 

    int intVal1; 
    int intVal2; 

    public MyCustomObject(int val1, int val2){ 
     intVal1 = val1; 
     intVal2 = val2; 
    } 

    public boolean equals(Object obj){ 
     return (((MyCustomObject)obj).intVal1 == this.intVal1) && 
       (((MyCustomObject)obj).intVal2 == this.intVal2); 
    } 

    public static void main(String a[]){ 
     MyCustomObject m1 = new MyCustomObject(3,5); 
     MyCustomObject m2 = new MyCustomObject(3,5); 
     MyCustomObject m3 = new MyCustomObject(4,5); 

     System.out.println(m1.equals(m2)); 
     System.out.println(m1.equals(m3)); 
    } 
} 

Qui l'uscita è vera, falsa esattamente come voglio che sia e mi preoccupo di override del metodo hashCode() a tutti. Ciò significa che l'override di hashCode() è un'opzione piuttosto che obbligatoria, come dicono tutti.

Voglio una seconda conferma.

+0

possibile duplicato di http://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when-equals-method-is-overriden-in-c – Oded

+4

Non un dupe di quello; questo è C#, questo è Java. (Il problema è molto simile, forse identico, ma comunque.) – Thomas

risposta

32

funziona per voi perché il codice non utilizza alcuna funzionalità (HashMap, HashTable), che ha bisogno del hashCode() API.

Tuttavia, non si sa se la propria classe (presumibilmente non scritta come una tantum) verrà successivamente chiamata in un codice che utilizza effettivamente i suoi oggetti come chiave hash, nel qual caso le cose verranno influenzate.

Come per il documentation for Object class:

Il contratto generale di hashCode è:

  • Ogni volta che viene richiamato sullo stesso oggetto più di una volta nel corso di un'esecuzione di un'applicazione Java, il hashCode il metodo deve restituire costantemente lo stesso numero intero, a condizione che non vengano modificate le informazioni utilizzate nei confronti degli uguali sull'oggetto. Questo numero intero non deve rimanere coerente da un'esecuzione di un'applicazione a un'altra esecuzione della stessa applicazione.

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

+1

E facoltativamente, 'hashCode' deve fare del proprio meglio per non restituire lo stesso numero intero su molti valori che potrebbero verificarsi insieme durante la stessa esecuzione del programma. Una volta ho avuto un bug di prestazioni che mi ci è voluto molto tempo per tracciare l'utilizzo dell'eguaglianza fisica insieme alla funzione di hash predefinita (strutturale). Questo uso soddisfa le condizioni di cui sopra, ma tutti i valori strutturalmente identici e fisicamente diversi sono stati sottoposti a hash allo stesso bucket. –

+0

@Pascal: corretto. Infatti il ​​documento continua: * 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 produrre risultati interi distinti per oggetti non uguali può migliorare le prestazioni degli hashtables **. – DVK

+0

"... Per quanto ragionevolmente pratico, il metodo hashCode definito dalla classe Object restituisce interi distinti per oggetti distinti (in genere viene implementato convertendo l'indirizzo interno dell'oggetto in un numero intero, ma questa tecnica di implementazione non è richiesto dal linguaggio di programmazione Java.) " – DVK

10

Perché HashMap/Hashtable cercherà prima l'oggetto di hashCode().

Se non sono uguali, hashmap asserirà che gli oggetti non sono gli stessi e che il ritorno non esiste nella mappa.

5

Il motivo per cui è necessario il numero @Override o entrambi, dipende dal modo in cui interagiscono con il resto dell'API.

Troverete che se si inserisce m1 in un HashSet<MyCustomObject>, quindi non contains(m2). Questo è comportamento incoerente e può causare un sacco di bug e caos.

La libreria Java ha un sacco di funzionalità. Al fine di farli funzionare per tu, devi giocare secondo le regole, e assicurarti che equals e siano coerenti è uno dei più importanti.

4

La maggior parte degli altri commenti già ti ha dato la risposta: è necessario farlo perché ci sono collezioni (es: HashSet, HashMap) che utilizza hashCode come un'ottimizzazione per istanze di oggetti "Index", un tali ottimizzazioni si aspetta che se: a.equals(b) ==>a.hashCode() == b.hashCode() (NOTA che l'inverso non regge).

Ma come informazioni aggiuntive è possibile fare questo esercizio:

class Box { 
    private String value; 
    /* some boring setters and getters for value */ 
    public int hashCode() { return value.hashCode(); } 
    public boolean equals(Object obj) { 
      if (obj != null && getClass().equals(obj.getClass()) { 
       return ((Box) obj).value.equals(value); 
      } else { return false; } 
    } 
} 

Il fare questo:

Set<Box> s = new HashSet<Box>(); 
Box b = new Box(); 
b.setValue("hello"); 
s.add(b); 
s.contains(b); // TRUE 
b.setValue("other"); 
s.contains(b); // FALSE 
s.iterator().next() == b // TRUE!!! b is in s but contains(b) returns false 

Che cosa si impara da questo esempio è che l'attuazione equals o hashCode con proprietà che possono essere cambiato (mutabile) è una pessima idea.

0

È importante soprattutto durante la ricerca di un oggetto utilizzando il suo valore hashCode() in una raccolta (ad esempio HashMap, HashSet e così via). Ogni oggetto restituisce un diverso valore hashCode(), pertanto è necessario sovrascrivere questo metodo per generare in modo coerente un valore hashCode in base allo stato dell'oggetto, in modo che l'algoritmo Collections individui i valori nella tabella hash.

Problemi correlati