Confronto di oggetti Java con equals () e hashcode ()

In questo Java Challenger imparerai come equals()e hashcode()combinare per rendere i confronti di oggetti efficienti e facili nei tuoi programmi Java. In poche parole, questi metodi funzionano insieme per verificare se due oggetti hanno gli stessi valori.  

Senza equals()e hashcode()dovremmo creare ifconfronti " " molto ampi , confrontando ogni campo di un oggetto. Ciò renderebbe il codice davvero confuso e difficile da leggere. Insieme, questi due metodi ci aiutano a creare un codice più flessibile e coeso.

Ottieni il codice sorgente di Java Challengers.

Sovrascrivere equals () e hashcode () in Java

L'override del metodo è una tecnica in cui il comportamento della classe o dell'interfaccia genitore viene riscritto (sovrascritto) nella sottoclasse per sfruttare il polimorfismo. Ogni Objectin Java include un equals()e un hashcode()metodo, ma devono essere sovrascritti per funzionare correttamente.

Per capire come funziona l'override con equals()e   hashcode(), possiamo studiarne l'implementazione nelle classi Java principali. Di seguito è riportato il equals()metodo nella Objectclasse. Il metodo verifica se l'istanza corrente è uguale a quella passata in precedenza Object.

 public boolean equals(Object obj) { return (this == obj); } 

Quando il hashcode()metodo non viene sovrascritto, Objectverrà richiamato il metodo predefinito nella classe. Questo è un metodo nativo , il che significa che verrà eseguito in un altro linguaggio come il C e restituirà del codice relativo all'indirizzo di memoria dell'oggetto. (Non è così importante sapere esattamente come funziona questo metodo a meno che tu non stia scrivendo codice JDK.)

 @HotSpotIntrinsicCandidate public native int hashCode(); 

Quando i metodi equals()e hashcode()non vengono sovrascritti, vedrai invece i metodi sopra richiamati. In questo caso, i metodi non soddisfano il vero scopo di equals()e hashcode(), ovvero controllare se due o più oggetti hanno gli stessi valori.

Di norma, quando si esegue equals()l' override, è necessario anche eseguire l'override hashcode().

Confronto di oggetti con uguale ()

Usiamo il equals()metodo per confrontare gli oggetti in Java. Per determinare se due oggetti sono uguali, equals()confronta i valori degli attributi degli oggetti:

 public class EqualsAndHashCodeExample { public static void main(String... equalsExplanation) { System.out.println(new Simpson("Homer", 35, 120) .equals(new Simpson("Homer",35,120))); System.out.println(new Simpson("Bart", 10, 120) .equals(new Simpson("El Barto", 10, 45))); System.out.println(new Simpson("Lisa", 54, 60) .equals(new Object())); } static class Simpson { private String name; private int age; private int weight; public Simpson(String name, int age, int weight) { this.name = name; this.age = age; this.weight = weight; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Simpson simpson = (Simpson) o; return age == simpson.age && weight == simpson.weight && name.equals(simpson.name); } } } 

Nel primo confronto, equals()confronta l'istanza dell'oggetto corrente con l'oggetto che è stato passato. Se i due oggetti hanno gli stessi valori, equals()tornerà true.

Nel secondo confronto, equals()controlla se l'oggetto passato è nullo o se è digitato come una classe diversa. Se è una classe diversa, gli oggetti non sono uguali.

Infine, equals()confronta i campi degli oggetti. Se due oggetti hanno gli stessi valori di campo, gli oggetti sono gli stessi.

Analisi dei confronti di oggetti

Vediamo ora i risultati di questi confronti nel nostro main()metodo. Innanzitutto, confrontiamo due Simpsonoggetti:

 System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120))); 

Gli oggetti qui sono identici, quindi il risultato sarà true.

Successivamente, confrontiamo Simpsonnuovamente due oggetti:

 System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45))); 

Gli oggetti qui sono quasi identici ma i loro nomi sono diversi: Bart e El Barto. Quindi il risultato sarà false.

Infine, confrontiamo un Simpsonoggetto e un'istanza della classe Object:

 System.out.println(new Simpson("Lisa", 54, 60).equals(new Object())); 

In questo caso il risultato sarà falseperché i tipi di classe sono diversi.

è uguale a () rispetto a ==

A prima vista, l' ==operatore e il equals()metodo possono sembrare fare la stessa cosa, ma in realtà funzionano in modo diverso. L' ==operatore confronta se due riferimenti a oggetti puntano allo stesso oggetto. Per esempio:

 System.out.println(homer == homer2); 

Nel primo confronto, abbiamo istanziato due diverse Simpsonistanze utilizzando l' newoperatore. Per questo motivo , le variabili homere homer2punteranno a Objectriferimenti diversi nell'heap di memoria. Quindi avremo falsecome risultato.

System.out.println(homer.equals(homer2)); 

Nel secondo confronto, sovrascriviamo il equals()metodo. In questo caso verranno confrontati solo i nomi. Poiché il nome di entrambi gli Simpsonoggetti è "Homer", il risultato sarà true.

Identificazione univoca degli oggetti con hashcode ()

Usiamo il hashcode()metodo per ottimizzare le prestazioni durante il confronto degli oggetti. L'esecuzione   hashcode()restituisce un ID univoco per ogni oggetto nel programma, il che semplifica notevolmente il compito di confrontare l'intero stato dell'oggetto.

Se il codice hash di un oggetto non è lo stesso del codice hash di un altro oggetto, non c'è motivo di eseguire il equals()metodo: sai solo che i due oggetti non sono la stessa cosa. D'altra parte, se il codice hash è lo stesso, è necessario eseguire il equals()metodo per determinare se i valori ei campi sono gli stessi.

Ecco un esempio pratico con hashcode().

 public class HashcodeConcept { public static void main(String... hashcodeExample) { Simpson homer = new Simpson(1, "Homer"); Simpson bart = new Simpson(2, "Homer"); boolean isHashcodeEquals = homer.hashCode() == bart.hashCode(); if (isHashcodeEquals) { System.out.println("Should compare with equals method too."); } else { System.out.println("Should not compare with equals method because " + "the id is different, that means the objects are not equals for sure."); } } static class Simpson { int id; String name; public Simpson(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o)  if (this == o) return true; if (o == null  @Override public int hashCode() { return id; } } } 

Un valore hashcode()che restituisce sempre lo stesso valore è valido ma non molto efficace. In questo caso il confronto tornerà sempre true, quindi il equals()metodo verrà sempre eseguito. Non vi è alcun miglioramento delle prestazioni in questo caso.  

Utilizzo di equals () e hashcode () con le raccolte

L' Setinterfaccia è responsabile di garantire che nessun elemento duplicato venga inserito in una Setsottoclasse. Le seguenti sono alcune delle classi che implementano l' Setinterfaccia:

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

Solo elementi unici possono essere inseriti in a Set, quindi se vuoi aggiungere un elemento alla HashSetclasse (per esempio), devi prima usare i metodi equals()e hashcode()per verificare che l'elemento sia unico. Se i metodi equals()e hashcode()non fossero sovrascritti in questo caso, rischieresti di inserire elementi duplicati nel codice.

Nel codice seguente, stiamo usando il addmetodo per aggiungere un nuovo elemento a un HashSetoggetto. Prima che il nuovo elemento venga aggiunto, HashSetcontrolla se l'elemento esiste già nella raccolta data:

 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; 

If the object is the same, the new element won’t be inserted.

Hash collections

Set isn’t the only collection that makes use of equals() and hashcode(). HashMap, Hashtable, and LinkedHashMap also require these methods. As a rule, if you see a collection that has the prefix of “Hash,” you can be sure that it requires overriding the hashcode() and equals() methods to make their features work properly.  

Guidelines for using equals() and hashcode()

You should only execute an equals() method for objects that have the same unique hashcode ID. You should not execute equals() when the hashcode ID is different.

Table 1. Hashcode comparisons

If the hashcode() comparison ... Then …
returns true execute equals()
returns false do not execute equals()

This principle is mainly used in Set or Hash collections for performance reasons.

Rules for object comparison

When a hashcode() comparison returns false, the equals() method must also return false. If the hashcode is different, then the objects are definitely not equal.

Table 2. Object comparison with hashcode()

When the hashcode comparison returns ... The equals() method should return ...
true true or false
false false

When the equals() method returns true, it means that the objects are equal in all values and attributes. In this case,  the hashcode comparison must be true as well.

Table 3. Object comparison with equals()

When the equals() method returns ... The hashcode() method should return ...
true true
false true or false

Take the equals() and hashcode() challenge!

It’s time to test your skills with the equals() and hashcode() methods.  Your goal in this challenge is to figure out the output of the two equals() method comparisons and guess the size of the Set collection.

To start, study the following code carefully:

 public class EqualsHashCodeChallenge { public static void main(String... doYourBest) { System.out.println(new Simpson("Bart").equals(new Simpson("Bart"))); Simpson overriddenHomer = new Simpson("Homer") { public int hashCode() { return (43 + 777) + 1; } }; System.out.println(new Simpson("Homer").equals(overriddenHomer)); Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge"))); set.add(new Simpson("Homer")); set.add(overriddenHomer); System.out.println(set.size()); } static class Simpson { String name; Simpson(String name) { this.name = name; } @Override public boolean equals(Object obj) { Simpson otherSimpson = (Simpson) obj; return this.name.equals(otherSimpson.name) && this.hashCode() == otherSimpson.hashCode(); } @Override public int hashCode() { return (43 + 777); } } } 

Remember, analyze the code first, guess the result, and then run the code. Your goal is to improve your skill with code analysis and absorb core Java concepts to make your code more powerful. Choose your answer before checking the correct answer below.

 A) true true 4 B) true false 3 C) true false 2 D) false true 3 

What just happened? Understanding equals() and hashcode()

In the first equals() method comparison, the result is true because the state of the object is exactly the same and the hashcode() method returns the same value for both objects.

In the second equals() method comparison, the hashcode() method is being overridden for the overridenHomer variable. The name is “Homer” for both Simpson objects, but the hashcode() method returns a different value for overriddenHomer. In this case, the final result from the the equals() method will be false because the method contains a comparison with the hashcode.

You might notice that the size of the collection is set to hold three Simpson objects. Let’s check this in a detailed way.

The first object in the set will be will be inserted normally:

 new Simpson("Homer"); 

The next object will be inserted normally, as well, because it holds a different value from the previous object:

 new Simpson("Marge"); 

Finally,  the following Simpson object has the same value as the first object. In this case the object won’t be inserted:

 set.add(new Simpson("Homer")); 

Come sappiamo, l' overridenHomeroggetto utilizza un valore hashcode diverso dalla normale Simpson(“Homer”)istanziazione. Per questo motivo, questo elemento verrà inserito nella collezione:

 overriddenHomer; 

Tasto di risposta

La risposta a questa sfidante Java è B . L'output sarebbe:

 true false 3 

Sfida video! Debug equals () e hashcode ()

Il debug è uno dei modi più semplici per assorbire completamente i concetti di programmazione migliorando allo stesso tempo il codice. In questo video puoi seguire mentre eseguo il debug e spiego Java equals()e la hashcode()sfida.