[java] Come assicurarsi che hashCode () sia coerente con equals ()?



Answers

Non entrerò nei dettagli dell'unicità di hashCode come Marc ha già affrontato. Per la tua classe People , devi prima decidere che cosa significhi l'uguaglianza di una persona. Forse l'uguaglianza si basa esclusivamente sul loro nome, forse si basa sul nome e sull'età. Sarà specifico per il dominio. Diciamo che l'uguaglianza è basata sul nome e sull'età. I tuoi equals forzati sembrerebbero

public boolean equals(Object obj) {
    if (this==obj) return true;
    if (obj==null) return false;
    if (!(getClass().equals(obj.getClass())) return false;
    Person other = (Person)obj;
    return (name==null ? other.name==null : name.equals(other.name)) &&
        age==other.age;
}

Ogni volta che si hashCode override è necessario eseguire l'override di hashCode . Inoltre, hashCode non può usare più campi nel suo calcolo rispetto a quelli equals . La maggior parte delle volte è necessario aggiungere o esclusivo o il codice hash dei vari campi (hashCode dovrebbe essere veloce da calcolare). Quindi un metodo di hashCode valido potrebbe essere simile a:

public int hashCode() {
    return (name==null ? 17 : name.hashCode()) ^ age;
}

Si noti che quanto segue non è valido in quanto utilizza un campo equals non (altezza). In questo caso due oggetti "uguali" potrebbero avere un diverso codice hash.

public int hashCode() {
    return (name==null ? 17 : name.hashCode()) ^ age ^ height;
}

Inoltre, è perfettamente valido per due oggetti non uguali avere lo stesso codice hash:

public int hashCode() {    
    return age;    
}

In questo caso, Jane 30 anni non è uguale a Bob 30 anni, ma entrambi i codici hash sono 30. Sebbene valido, ciò non è auspicabile per le prestazioni nelle raccolte basate su hash.

Question

Quando si esegue l'override della funzione equals () di java.lang.Object, i javadoc suggeriscono 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.

Il metodo hashCode () deve restituire un numero intero univoco per ogni oggetto (questo è facile da fare quando si confrontano gli oggetti in base alla posizione della memoria, è sufficiente restituire l'indirizzo intero univoco dell'oggetto)

Come dovrebbe essere sovrascritto un metodo hashCode () in modo che restituisca un intero univoco per ogni oggetto basato solo sulle proprietà dell'oggetto?


public class People{
   public String name;
   public int age;

   public int hashCode(){
      // How to get a unique integer based on name and age?
   }
}
/*******************************/
public class App{
   public static void main( String args[] ){
       People mike = new People();
       People melissa = new People();
       mike.name = "mike";
       mike.age = 23;
       melissa.name = "melissa";
       melissa.age = 24;
       System.out.println( mike.hasCode() );  // output?
       System.out.println( melissa.hashCode(); // output?
   }
}



L'unico obbligo contrattuale per hashCode è che sia coerente . I campi utilizzati nella creazione del valore hashCode devono essere uguali o un sottoinsieme dei campi utilizzati nel metodo equals. Ciò significa che restituire 0 per tutti i valori è valido, sebbene non efficiente.

Si può verificare se hashCode è consistente tramite un test unitario. Ho scritto una classe astratta chiamata EqualityTestCase , che esegue una manciata di controlli hashCode. È sufficiente estendere il caso di test e implementare due o tre metodi di produzione. Il test fa un lavoro molto crudo di test se l'hashCode è efficiente.




In generale il codice hash non può essere univoco, poiché ci sono più valori di possibili codici hash (interi). Un buon codice hash distribuisce bene i valori sopra gli interi. Un cattivo potrebbe sempre dare lo stesso valore ed essere sempre logicamente corretto, porterebbe solo a tabelle hash inaccettabilmente inefficienti.

I valori uguali devono avere lo stesso valore di hash per il corretto funzionamento delle tabelle hash. Altrimenti potresti aggiungere una chiave a una tabella hash, quindi provare a cercarla con lo stesso valore con un diverso codice hash e non trovarla. Oppure potresti mettere un valore uguale con un diverso codice hash e avere due valori uguali in posti diversi nella tabella hash.

In pratica, di solito si seleziona un sottoinsieme dei campi da prendere in considerazione sia nel metodo hashCode () sia nel metodo equals ().




Esiste una nozione di chiave aziendale, che determina l'univocità delle istanze separate dello stesso tipo. Ogni specifico tipo (classe) che modella un'entità separata dal dominio di destinazione (ad esempio veicolo in un sistema di flotta) dovrebbe avere una chiave aziendale, che è rappresentata da uno o più campi di classe. Metodi equals () e hasCode () dovrebbero essere entrambi implementati utilizzando i campi, che costituiscono una chiave aziendale. Ciò garantisce che entrambi i metodi siano coerenti tra loro.




Links