2012-06-20 16 views
22
class temp { 
int id; 

public int getId() { 
    return id; 
} 

temp(int id) { 
    this.id = id; 
} 

public void setId(int id) { 
    this.id = id; 
} 

@Override 
public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    temp other = (temp) obj; 
    if (id != other.id) 
     return false; 
    return true; 
} 
} 

public class testClass { 

    public static void main(String[] args) { 
     temp t1 = new temp(1); 
     temp t2 = new temp(1); 
     System.out.println(t1.equals(t2)); 
     Set<temp> tempList = new HashSet<temp>(2); 
     tempList.add(t1); 
     tempList.add(t2); 
     System.out.println(tempList); 
} 

Il programma aggiunge entrambi gli elementi al Set. All'inizio sono rimasto scioccato perché, aggiungendo i metodi da impostare, viene invocato il metodo equals.HashSet consente l'inserimento di elementi duplicati se hashCode() non viene sovrascritto

Ma poi ho calpestato il metodo hashCode:

@Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + id; 
     return result; 
    } 

E poi non ho aggiunto. Questo è sorprendente poiché il metodo Javadoc di Set e add() dice che controlla solo equals() durante l'aggiunta nel Set.

e questo è il Javadoc per add():

/** 
    * Adds the specified element to this set if it is not already present. 
    * More formally, adds the specified element <tt>e</tt> to this set if 
    * this set contains no element <tt>e2</tt> such that 
    * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>. 
    * If this set already contains the element, the call leaves the set 
    * unchanged and returns <tt>false</tt>. 
    * 
    * @param e element to be added to this set 
    * @return <tt>true</tt> if this set did not already contain the specified 
    * element 
    */ 
    public boolean add(E e) { 
     return map.put(e, PRESENT)==null; 
    } 

poi ho capito che il HashSet è implementato come un HashMap e nella mappa, la hashCode dell'oggetto viene utilizzato come chiave. Quindi, li sta trattando con chiavi diverse se non si sostituisce hashCode.

Non dovrebbe essere presente nella documentazione del metodo add() o di HashSet?

+2

L'intera ragione per cui esiste la funzione hashCode() è per le raccolte basate su hash. Come dovrebbe una collezione "sapere" quale funzione hash usare se non la si definisce? –

+0

Ho scoperto che il metodo equals() della classe temp non viene mai chiamato in questo caso (provato sysout), quindi la JVM richiama il valore predefinito equals() se non abbiamo hashCode() implementato per soddisfare il contratto di JVM specifica. Interessante !!!!!!! –

+2

Chiama solo 'equals' se i' hashCode() 's sono uguali. Non chiama il valore predefinito uguale se lo si sostituisce. –

risposta

20

È documentato. Vedere la documentazione per java.lang.Object, dove si dice sul hashCode():

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

Inoltre seguente si trova nella documentazione per il metodo Object.equals(Object):

noti che è generalmente necessario ridefinire il metodo hashCode quando questo metodo viene sovrascritto, in modo da mantenere la generale contratto per il metodo hashCode, che afferma che gli oggetti uguali devono avere uguale codice hash.

In altre parole, se con la tua classe quando instanceA.equals(instanceB) == true e instanceA.hashCode() != istanceB.hashCode() si sono infatti violare il contratto della classe Object.

+1

Ho sempre letto che equals() e hashCode() devono essere entrambi sovrascritti, ma non avrei mai saputo che non farlo avrebbe prodotto risultati imprevedibili. Inoltre, poiché la documentazione del metodo Set e del metodo add() specifica in modo specifico che è necessario eseguire l'override del metodo equals ma nessuna menzione di hashCode(). Il contratto esprime tutto. –

+0

@DhwaneetBhatt Sono contento di essere stato in grado di essere di aiuto. Infatti, quando si memorizzano oggetti negli insiemi di Java, l'implementazione di entrambi i metodi è importante. Non sto dicendo che dovrebbe essere trascurato quando non vi è alcuna necessità apparente o immediata di farlo. Un'implementazione corretta (come da contratto) può, tuttavia, essere non banale da immaginare. – Kallja

+0

Grazie per la risposta !!! –

14

Basta dare un'occhiata anche a equals() documentazione:

Si noti che è generalmente necessario eseguire l'override del metodo hashCode ogni volta che viene sovrascritto questo metodo, in modo da mantenere il contratto generale per il metodo hashCode, in cui si afferma che gli oggetti uguali devono avere uguali codici hash.

Il fatto è che equals() e hashCode() sono fortemente collegati. Entrambi devono essere sempre considerati quando si lavora con uno di essi per evitare questi problemi di coerenza.

+0

L'ho appena imparato nel modo più duro oggi :) –

+0

Jack in questione perché uguale a(); il metodo ha il tipo Object nel parametro? anche in questo caso, perché non indirizzare il parametro del tipo di classe temp? – UnKnown

8

Se si esegue l'override di equals() è necessario che sostituisca anche hashCode().

Sono presenti alcune restrizioni sul comportamento di equals() e hashCode(), che sono enumerati nella documentazione di Object. In particolare, il metodo equals() deve presentare le seguenti caratteristiche:

  • Symmetry: Per due riferimenti A e B, a.equals (b) se e solo se b.equals (a)
  • Riflessività : Per tutti i riferimenti non null, a.equals (a)
  • Transitività: se a.equals (b) e b.equals (c), quindi a.equals (c)
  • Coerenza con hashCode(): Due oggetti uguali devono avere lo stesso valore hashCode()

Vedere this per ulteriori dettagli.

+0

Grazie per il collegamento –

+1

Quanto è interessante che ogni cosa sia ovunque così ispirata dalla Matematica;) –

1

Essi (i ragazzi javadoc) potrebbe avere pre-assunto che quando dicono (la documentazione di add() metodo HashSet)

il hashCode() è intrinsecamente uguale per entrambi.

+1

Concordato, sebbene la presunzione sia valida poiché uno che usa un Set dovrebbe avere familiarità con i metodi correlati, come Object.equals (Object), che secondo la documentazione del rispettivo metodo è fortemente legato al metodo Object.hashCode(). – Kallja

+0

@Jarkko: consultare l'anser aggiornato –

Problemi correlati