Jämföra Java-objekt med lika () och hashkod ()

I denna Java Challenger lär du dig hur equals()och hashcode()kombinera för att göra objektjämförelser effektiva och enkla i dina Java-program. Enkelt uttryckt fungerar dessa metoder tillsammans för att verifiera om två objekt har samma värden.  

Utan equals()och hashcode()vi skulle behöva skapa mycket stora " if" jämförelser, jämföra varje fält från ett objekt. Detta skulle göra koden riktigt förvirrande och svårläst. Tillsammans hjälper dessa två metoder oss att skapa mer flexibel och sammanhängande kod.

Hämta Java Challengers källkod.

Åsidosättande är lika med () och hashkod () i Java

Metodöverstyrning är en teknik där föräldraklassens eller gränssnittets beteende skrivs igen (åsidosätts) i underklassen för att dra nytta av polymorfism. Alla Objecti Java innehåller en equals()och en hashcode()metod, men de måste åsidosättas för att fungera korrekt.

För att förstå hur övergripande fungerar med equals()och   hashcode()kan vi studera deras implementering i Java-klasserna. Nedan följer equals()metoden i Objectklassen. Metoden kontrollerar om den aktuella instansen är densamma som den tidigare passerade Object.

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

När hashcode()metoden inte åsidosätts kommer standardmetoden i Objectklassen att åberopas. Detta är en infödd metod , vilket innebär att den kommer att köras på ett annat språk som C, och kommer att returnera lite kod angående objektets minnesadress. (Det är inte så viktigt att veta exakt hur den här metoden fungerar om du inte skriver JDK-kod.)

 @HotSpotIntrinsicCandidate public native int hashCode(); 

När equals()och hashcode()metoderna inte åsidosätts ser du ovanstående metoder i stället. I detta fall är de metoder som inte uppfyller det verkliga syftet med equals()och hashcode()som är att kontrollera om två eller flera objekt har samma värderingar.

Som regel equals()måste du också åsidosätta när du åsidosätter hashcode().

Jämföra objekt med lika ()

Vi använder equals()metoden för att jämföra objekt i Java. För att avgöra om två objekt är desamma, equals()jämför värdena för objekten attribut:

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

I den första jämförelsen equals()jämför den aktuella objektinstansen med det objekt som har skickats. Om de två objekten har samma värden equals()kommer de att returneras true.

I den andra jämförelsen equals()kontrollerar du om det skickade objektet är null eller om det skrivs som en annan klass. Om det är en annan klass är objekten inte lika.

Slutligen equals()jämför objektens fält. Om två objekt har samma fältvärden, är objekten desamma.

Analysera objektjämförelser

Låt oss nu se resultaten av dessa jämförelser i vår main()metod. Först jämför vi två Simpsonobjekt:

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

Objekten här är identiska, så resultatet blir true.

Därefter jämför vi två Simpsonobjekt igen:

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

Objekten här är nästan identiska men deras namn är olika: Bart och El Barto. Därför blir resultatet false.

Slutligen, låt oss jämföra ett Simpsonobjekt och en instans av klassen Object:

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

I det här fallet kommer resultatet att falseklasstyperna är olika.

är lika med () kontra ==

Vid första anblicken verkar ==operatören och equals()metoden göra samma sak, men i själva verket fungerar de annorlunda. Den ==Operatören jämför om två objektreferenser pekar på samma objekt. Till exempel:

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

I den första jämförelsen instanserade vi två olika Simpsoninstanser med newoperatören. På grund av detta pekar variablerna homeroch homer2pekar på olika Objectreferenser i minneshögen. Så vi får falsesom resultat.

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

I den andra jämförelsen åsidosätter vi equals()metoden. I det här fallet kommer endast namnen att jämföras. Eftersom namnet på båda Simpsonobjekten är "Homer" blir resultatet true.

Unikt identifiering av objekt med hashcode ()

Vi använder hashcode()metoden för att optimera prestanda när vi jämför objekt. Exekvering   hashcode()returnerar ett unikt ID för varje objekt i ditt program, vilket gör uppgiften att jämföra hela objektets tillstånd mycket lättare.

Om ett objekts hashkod inte är detsamma som ett annat objekts hashkod, finns det ingen anledning att utföra equals()metoden: du vet bara att de två objekten inte är desamma. Å andra sidan, om hashkoden är densamma, måste du utföra equals()metoden för att avgöra om värdena och fälten är desamma.

Här är ett praktiskt exempel med 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()som alltid returnerar samma värde är giltigt men inte särskilt effektivt. I detta fall kommer jämförelsen alltid att återvända true, så equals()metoden kommer alltid att köras. Det finns ingen prestandaförbättring i det här fallet.  

Använda lika () och hashkod () med samlingar

Den SetGränssnittet är ansvarig för att inga dubbla element kommer att införas i en Setunderklass. Följande är några av de klasser som implementerar Setgränssnittet:

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

Endast unika element kan sättas in i en Set, så om du vill lägga till ett element till HashSetklass (till exempel), måste du först använda equals()och hashcode()metoder för att kontrollera att elementet är unik. Om metoderna equals()och hashcode()metoderna inte åsidosätts i det här fallet riskerar du att infoga dubbletter i koden.

I koden nedan använder vi addmetoden för att lägga till ett nytt element i ett HashSetobjekt. Innan det nya elementet läggs till HashSetkontrollerar du om elementet redan finns i den givna samlingen:

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

Som vi vet overridenHomeranvänder objektet ett annat hashkodvärde från normal Simpson(“Homer”)instantiering. Av detta skäl kommer detta element att införas i samlingen:

 overriddenHomer; 

Svarsknapp

Svaret på denna Java utmanare är B . Resultatet skulle vara:

 true false 3 

Videoutmaning! Felsökning är lika med () och hashcode ()

Felsökning är ett av de enklaste sätten att helt absorbera programmeringskoncept och samtidigt förbättra din kod. I den här videon kan du följa med medan jag felsöker och förklarar Java equals()och hashcode()utmaningen.