Сравняване на Java обекти с equals () и hashcode ()

В този Java Challenger ще научите как equals()и hashcode()комбинирате, за да направите сравненията на обекти ефективни и лесни във вашите Java програми. Най-просто казано, тези методи работят заедно, за да проверят дали два обекта имат еднакви стойности.  

Без equals()и hashcode()би трябвало да създадем много големи " if" сравнения, сравнявайки всяко поле от обект. Това би направило кода наистина объркващ и труден за четене. Заедно тези два метода ни помагат да създадем по-гъвкав и сплотен код.

Вземете изходния код на Java Challengers.

Замяна на equals () и hashcode () в Java

Замяна на метод е техника, при която поведението на родителския клас или интерфейс се записва отново (заменено) в подкласа, за да се възползва от полиморфизма. Всеки Objectв Java включва equals()и hashcode()метод, но те трябва да бъдат заменени, за да работят правилно.

За да разберем как заместването работи с equals()и   hashcode(), можем да изучим тяхното изпълнение в основните Java класове. По-долу е equals()методът в Objectкласа. Методът проверява дали текущият екземпляр е същият като предишния Object.

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

Когато hashcode()методът не е заменен, Objectще бъде извикан методът по подразбиране в класа. Това е естествен метод , което означава, че ще бъде изпълнен на друг език като C и ще върне някакъв код по отношение на адреса на паметта на обекта. (Не е толкова важно да знаете точно как работи този метод, освен ако не пишете JDK код.)

 @HotSpotIntrinsicCandidate public native int hashCode(); 

Когато equals()и hashcode()методи не са отменено, ще видите по-горе методи, изтъкнати вместо. В този случай методите не изпълняват реалната цел на equals()и hashcode(), което е да проверят дали два или повече обекта имат еднакви стойности.

Като правило, когато замените, equals()вие също трябва да замените hashcode().

Сравняване на обекти с равно ()

Използваме equals()метода за сравняване на обекти в Java. За да се определи дали два обекта са еднакви, equals()сравнява стойностите на атрибутите на обектите:

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

При първото сравнение equals()сравнява текущия екземпляр на обект с подадения обект. Ако двата обекта имат еднакви стойности, equals()ще се върнат true.

При второто сравнение equals()проверява дали подаденият обект е нулев или е въведен като различен клас. Ако това е различен клас, тогава обектите не са равни.

И накрая, equals()сравнява полетата на обектите. Ако два обекта имат еднакви полеви стойности, тогава обектите са еднакви.

Анализиране на сравнения на обекти

Сега, нека разгледаме резултатите от тези сравнения в нашия main()метод. Първо сравняваме два Simpsonобекта:

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

Обектите тук са идентични, така че резултатът ще бъде true.

След това отново сравняваме два Simpsonобекта:

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

Обектите тук са почти идентични, но имената им са различни: Барт и Ел Барто. Следователно резултатът ще бъде false.

И накрая, нека сравним Simpsonобект и екземпляр на класа Object:

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

В този случай резултатът ще бъде, falseзащото типовете класове са различни.

е равно () спрямо ==

На пръв поглед може да изглежда , че ==операторът и equals()методът правят едно и също нещо, но в действителност те работят по различен начин. На ==оператора сравнява дали две референции на обекти точка за същия обект. Например:

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

При първото сравнение ние създадохме два различни Simpsonекземпляра, използвайки newоператора. Поради това променливите homerи homer2ще сочат към различни Objectпрепратки в купчината памет. Така че ще имаме falseкато резултат.

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

При второто сравнение ние заместваме equals()метода. В този случай ще се сравняват само имената. Тъй като името на двата Simpsonобекта е „Homer“, резултатът ще бъде true.

Уникално идентифициране на обекти с hashcode ()

Използваме hashcode()метода за оптимизиране на производителността при сравняване на обекти. Изпълнението   hashcode()връща уникален идентификатор за всеки обект във вашата програма, което значително улеснява задачата за сравняване на цялото състояние на обекта.

Ако хеш-кодът на обекта не е същият като хеш-кода на друг обект, няма причина да се изпълни equals()методът: просто знаете, че двата обекта не са еднакви. От друга страна, ако хеш-кодът е същият, тогава трябва да изпълните equals()метода, за да определите дали стойностите и полетата са еднакви.

Ето практически пример с 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; } } } 

А, hashcode()което винаги връща една и съща стойност, е валидно, но не е много ефективно. В този случай сравнението винаги ще се върне true, така че equals()методът винаги ще бъде изпълнен. В този случай няма подобрение на производителността.  

Използване на equals () и hashcode () с колекции

В SetИнтерфейсът е отговорен за гарантиране не дублиращи се елементи трябва да бъдат вмъкнати в Setподклас. По-долу са някои от класовете, които реализират Setинтерфейса:

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

Само уникални елементи могат да се вмъкват в a Set, така че ако искате да добавите елемент към HashSetкласа (например), първо трябва да използвате equals()и hashcode()методите, за да проверите дали елементът е уникален. Ако equals()и hashcode()методи не са били игнорирани, в този случай, може би рискувал да поставите дублиращи се елементи в кода.

В кода по-долу използваме addметода за добавяне на нов елемент към HashSetобект. Преди да бъде добавен новият елемент, HashSetпроверява дали елементът вече съществува в дадената колекция:

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

Както знаем, overridenHomerобектът използва различна стойност на хеш-код от нормалната Simpson(“Homer”)инстанция. Поради тази причина този елемент ще бъде вмъкнат в колекцията:

 overriddenHomer; 

Клавиш за отговор

Отговорът на този Java претендент е B . Резултатът ще бъде:

 true false 3 

Видео предизвикателство! Отстраняването на грешки е равно () и хеш-код ()

Отстраняването на грешки е един от най-лесните начини за пълно усвояване на концепциите за програмиране, като същевременно подобрявате кода си. В това видео можете да продължите, докато отстранявам грешки и обяснявам Java equals()и hashcode()предизвикателството.