Usporedba Java objekata s equals () i hashcode ()

U ovom Java Challengeru naučit ćete kako equals()i hashcode()kombinirati kako bi usporedbe objekata bile učinkovite i jednostavne u vašim Java programima. Jednostavno rečeno, ove metode rade zajedno kako bi provjerile imaju li dva objekta iste vrijednosti.  

Bez equals()i hashcode()morali bismo stvoriti vrlo velike ifusporedbe, uspoređujući svako polje od objekta. To bi kod učinilo zbunjujućim i teškim za čitanje. Zajedno, ove dvije metode pomažu nam u stvaranju fleksibilnijeg i kohezivnijeg koda.

Nabavite izvorni kod Java Challengersa.

Nadjačavanje equals () i hashcode () u Javi

Nadjačavanje metode je tehnika u kojoj se ponašanje nadređene klase ili sučelja ponovo zapisuje (poništava) u podrazred kako bi se iskoristila prednost polimorfizma. Sve Objectu Javi uključuju equals()i hashcode()metodu, ali moraju biti nadjačane da bi ispravno radile.

Da bismo razumjeli kako nadjačavanje funkcionira s equals()i   hashcode(), možemo proučiti njihovu implementaciju u temeljne Java klase. Ispod je equals()metoda u Objectrazredu. Metoda provjerava je li trenutna instanca ista kao prethodno proslijeđena Object.

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

Kada se hashcode()metoda ne poništi, pozvat će se zadana metoda u Objectklasi. Ovo je izvorna metoda , što znači da će se izvršiti na drugom jeziku poput C i vratit će neki kod u vezi s memorijskom adresom objekta. (Nije toliko važno znati točno kako ova metoda funkcionira, osim ako ne pišete JDK kôd.)

 @HotSpotIntrinsicCandidate public native int hashCode(); 

Kada metode equals()and hashcode()i nisu nadjačane, umjesto njih vidjet ćete kako se pozivaju gore navedene metode. U ovom slučaju, metode ne ispunjavaju stvarnu svrhu equals()i hashcode(), a to je provjera imaju li dva ili više objekata iste vrijednosti.

U pravilu, kada poništite equals(), morate i poništiti hashcode().

Usporedba objekata s jednakim ()

equals()Metodu koristimo za usporedbu objekata u Javi. Da bi se utvrdilo jesu li dva objekta ista, equals()uspoređuje vrijednosti atributa predmeta:

 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); } } } 

U prvoj usporedbi equals()uspoređuje trenutnu instancu objekta s objektom koji je proslijeđen. Ako dva objekta imaju iste vrijednosti, equals()vratit će se true.

U drugoj usporedbi equals()provjerava je li proslijeđeni objekt null ili je upisan kao druga klasa. Ako se radi o drugoj klasi, objekti nisu jednaki.

Na kraju, equals()uspoređuje polja objekata. Ako dva objekta imaju iste vrijednosti polja, tada su objekti isti.

Analizirajući usporedbe objekata

Pogledajmo sada rezultate ovih usporedbi u našoj main()metodi. Prvo uspoređujemo dva Simpsonobjekta:

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

Ovdje su objekti identični, pa će rezultat biti true.

Zatim Simpsonponovno uspoređujemo dva objekta:

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

Ovdje su predmeti gotovo identični, ali njihova su imena različita: Bart i El Barto. Stoga će rezultat biti false.

Na kraju, usporedimo Simpsonobjekt i instancu klase Object:

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

U ovom će slučaju rezultat biti falsezato što su vrste klasa različite.

jednako () naspram ==

Na prvi se pogled čini da ==operater i equals()metoda rade isto, ali u stvari djeluju drugačije. ==Operater uspoređuje li dva objekta reference upućuju na isti objekt. Na primjer:

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

U prvoj usporedbi napravili smo dvije različite Simpsoninstance pomoću newoperatora. Zbog toga će varijable homeri homer2ukazivati ​​na različite Objectreference u hrpi memorije. Pa ćemo imati falsekao rezultat.

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

U drugoj usporedbi nadjačavamo equals()metodu. U ovom će se slučaju uspoređivati ​​samo imena. Budući da je ime oba Simpsonobjekta "Homer", rezultat će biti true.

Jedinstveno identificiranje objekata s hashcodeom ()

hashcode()Metodu koristimo za optimizaciju izvedbe prilikom usporedbe objekata. Izvršenje   hashcode()vraća jedinstveni ID za svaki objekt u vašem programu, što znatno olakšava zadatak usporedbe cijelog stanja objekta.

Ako hashcode objekta nije isti kao hashcode drugog objekta, nema razloga za izvršavanje equals()metode: samo znate da dva objekta nisu ista. S druge strane, ako je hash broj je isti, onda morate izvršiti equals()metodom kako bi se utvrdilo da li su vrijednosti i polja su isti.

Evo praktičnog primjera s 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; } } } 

A hashcode()koji uvijek vraća istu vrijednost vrijedi, ali nije vrlo učinkovit. U tom će se slučaju usporedba uvijek vratiti true, tako da equals()će se metoda uvijek izvršiti. U ovom slučaju nema poboljšanja performansi.  

Korištenje equals () i hashcode () s kolekcijama

SetSučelje je odgovoran za osiguranje ne duple elemente će se umetnuti u Setpodrazred. Slijede neke od klasa koje implementiraju Setsučelje:

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

Samo jedinstveni elementi mogu biti umetnuta u Set, pa ako želite dodati element na HashSetklase (na primjer), najprije morate koristiti equals()i hashcode()metode kako bi potvrdili da je element je jedinstven. Da u ovom slučaju metode equals()and i hashcode()nisu nadjačane, riskirali biste umetanje dvostrukih elemenata u kod.

U donjem kodu koristimo addmetodu za dodavanje novog elementa u HashSetobjekt. Prije dodavanja novog elementa HashSetprovjerava postoji li element u datoj zbirci:

 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")); 

Kao što znamo, overridenHomerobjekt koristi različitu hashcode vrijednost od uobičajene Simpson(“Homer”)instancije. Iz tog će razloga ovaj element biti umetnut u zbirku:

 overriddenHomer; 

Kljucni odgovor

Odgovor na ovo Java izazivač je B . Rezultat bi bio:

 true false 3 

Video izazov! Otklanjanje pogrešaka jednako je () i hashcode ()

Otklanjanje pogrešaka jedan je od najlakših načina za potpuno apsorbiranje programskih koncepata uz istovremeno poboljšanje koda. U ovom videozapisu možete pratiti dok ja rješavam pogreške i objašnjavam Javu equals()i hashcode()izazov.