Правила сравнения объектов
Когда сравнение hashcode() возвращает false, метод equals() также должен возвращать значение false. Если хэш-код отличается, то объекты не равны.
Таблица 2. Сравнение объектов с помощью hashcode()
Когда сравнение хэш-кодов возвращает… | метод equals() должен возвращать … |
True | true или false |
False | False |
Когда метод equals() возвращает true, то объекты равны во всех значениях и атрибутах. В этом случае сравнение хэш-кодов должно возвращать true.
Таблица 3. Сравнение объектов с помощью equals()
Когда метод equals() возвращает … | метод hashcode() должен возвращать… |
True | True |
False | true или false |
Использование equals() и hashcode() с коллекциями
Интерфейс Set отвечает за то, чтобы в подкласс Set не было повторяющихся элементов. Ниже перечислены часто используемые классы, реализующие интерфейс Set:
- HashSet
- TreeSet
- LinkedHashSet
- CopyOnWriteArraySet
В Set можно использовать только уникальные элементы. Поэтому, если вы хотите добавить элемент в класс HashSet, сначала необходимо использовать equals() и hashcode(). Но если эти методы не будут переопределены, вы рискуете вставить в код повторяющиеся элементы.
В приведенном ниже примере для добавления нового элемента в объект HashSet используется метод add. Перед добавлением нового элемента HashSet проверяет, существует ли элемент в данной коллекции:
if(e.hash==hash&&((k =e.key)==key||(key!=null&&key.equals(k)))) break; p = e;
Если объект тот же, новый элемент не будет вставлен.
Идентификация объектов с помощью hashcode ()
Мы используем метод hashcode() для оптимизации производительности при сравнении объектов. Выполнение hashcode() возвращает уникальный идентификатор для каждого объекта в программе. Что значительно облегчает реализацию.
Если хэш-код объекта не совпадает с хэш-кодом другого объекта, нет причин для выполнения метода equals(). Вы просто будете знать, что два объекта не совпадают. Но если хэш-код одинаков, то нужно выполнить equals(), чтобы определить, совпадают ли значения и поля объектов.
Практический пример использования hashcode().
publicclassHashcodeConcept{ publicstaticvoid main(String...hashcodeExample){ Simpson homer =newSimpson(1,"Homer"); Simpsonbart=newSimpson(2,"Homer"); booleanisHashcodeEquals=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, thatmeans the objects are not equals for sure."); } } staticclassSimpson{ int id; String name; publicSimpson(int id,String name){ this.id = id; this.name = name; } @Override publicboolean equals(Object o){ if(this== o)returntrue; if(o ==null||getClass()!=o.getClass())returnfalse; Simpsonsimpson=(Simpson) o; return id == simpson.id && name.equals(simpson.name); } @Override publicinthashCode(){ return id; } } }
hashcode(), который всегда возвращает одно и то же значение, не очень эффективен. В этом случае сравнение всегда будет возвращать true, поэтому метод equals() будет выполняться всегда. Поэтому улучшить производительность кода не получится.
Переопределение equals() и hashcode() в Java
Переопределение — это способ, при котором поведение родительского класса или интерфейса повторно прописывается (переопределяется) в подклассе. Каждый Object в Java включает в себя метод equals() и hashcode(). Но для корректной работы они должны быть переопределены.
Ниже приведен метод equals() в классе Object. Метод проверяет, совпадает ли текущий экземпляр с ранее переданным объектом.
publicboolean equals(Objectobj){ return(this==obj); }
Если hashcode() не переопределен, будет вызван метод, используемый по умолчанию в классе Object. Это означает, что он будет выполнен на другом языке, таком как C, и вернет некоторый результат относительно адреса памяти объекта.
@HotSpotIntrinsicCandidate publicnativeinthashCode();
Если equals() и hashcode() не переопределены, вместо них вы увидите приведенные выше методы. В этом случае методы не выполняют задачу equals() и hashcode()— проверку, имеют ли два (или более) объекта одинаковые значения.
Анализ сравнений объектов
Рассмотрим результаты этих сравнений в методе main(). Сначала мы сравниваем два объекта Simpson:
System.out.println(newSimpson("Homer",35,120).equals(newSimpson("Homer",35,120)));
Объекты идентичны, поэтому результат будет true.
Затем снова сравниваем два объекта Simpson:
System.out.println(newSimpson("Bart",10,45).equals(newSimpson("El Barto",10,45)));
Объекты почти идентичны, но поля name имеют разные значения (Bart и El Barto). Поэтому результат будет false.
Сравним объект Simpson и экземпляр класса Object:
System.out.println(newSimpson("Lisa",54,60).equals(newObject()));
В этом случае результат будет false, потому что типы классов разные.
Что нужно помнить о equals() и hashcode()
- Рекомендованная практика — всегда переопределять методы equals() и hashcode() в POJO.
- Используйте эффективный алгоритм для генерации уникального хэш-кода.
- При переопределении метода equals() всегда переопределяйте hashcode().
- Метод equals() должен сравнивать все значения полей.
- Метод hashcode() может быть идентификатором POJO.
- Если equals() и hashcode() не переопределяются при использовании хэш-коллекций, коллекция будет иметь повторяющиеся элементы.
Пожалуйста, опубликуйте ваши отзывы по текущей теме статьи. За комментарии, подписки, лайки, отклики, дизлайки огромное вам спасибо!
Вадим Дворниковавтор-переводчик статьи «Java Challengers #4: Comparing Java objects with equals() and hashcode()»
Сравнение объектов с помощью метода equals ()
Мы используем метод equals() для сравнения объектов в Java. Чтобы определить, совпадают ли два объекта, equals() сравнивает значения атрибутов объектов:
publicclassEqualsAndHashCodeExample{ publicstaticvoid main(String...equalsExplanation){ System.out.println(newSimpson("Homer",35,120) .equals(newSimpson("Homer",35,120))); System.out.println(newSimpson("Bart",10,120) .equals(newSimpson("El Barto",10,45))); System.out.println(newSimpson("Lisa",54,60) .equals(newObject())); } staticclassSimpson{ privateString name; privateint age; privateintweight; publicSimpson(String name,int age,intweight){ this.name = name; this.age= age; this.weight=weight; } @Override publicboolean equals(Object o){ if(this== o){ returntrue; } if(o ==null||getClass()!=o.getClass()){ returnfalse; } Simpsonsimpson=(Simpson) o; return age ==simpson.age&& weight==simpson.weight&& name.equals(simpson.name); } } }
В первом случае equals() сравнивает текущий экземпляр объекта с переданным объектом. Если оба имеют одинаковые значения, equals() вернет true.
Во втором сравнении equals() проверяет, является ли переданный объект пустым, или его тип принадлежит к другому классу. Если это другой класс, то объекты не равны.
Наконец, equals() сравнивает поля объектов. Если два объекта имеют одинаковые значения полей, то объекты одинаковы.
equals () или ==
Может показаться, что оператор == и метод equals() делают то же самое. Но на самом деле они работают по-разному. Оператор == сравнивает, указывают ли две ссылки на один и тот же объект.
Например:
System.out.println(homer == homer2);
В первом сравнении мы создали два разных экземпляра Simpson, используя оператор new. Из-за этого переменные homer и homer2 будут указывать на разные объекты в памяти. В результате мы получим false.
System.out.println(homer.equals(homer2));
Во втором сравнении мы переопределяем метод equals(). В этом случае будут сравниваться только поля name. Поскольку name обоих объектов Simpson является «Homer», результат true.
Выполните задание на использование equals() и hashcode()!
Пришло время проверить свои навыки. Нужно получить результат двух сравнений метода equals() и узнать размер коллекции Set.
Для начала внимательно изучите приведенный ниже код:
publicclassEqualsHashCodeChallenge{ publicstaticvoid main(String...doYourBest){ System.out.println(newSimpson("Bart").equals(newSimpson("Bart"))); Simpson overriddenHomer =newSimpson("Homer"){ publicinthashCode(){ return(43+777)+1; } }; System.out.println(newSimpson("Homer").equals(overriddenHomer)); Setset=newHashSet(Set.of(newSimpson("Homer"),newSimpson("Marge"))); set.add(newSimpson("Homer")); set.add(overriddenHomer); System.out.println(set.size()); } staticclassSimpson{ String name; Simpson(String name){ this.name = name; } @Override publicboolean equals(Objectobj){ SimpsonotherSimpson=(Simpson)obj; returnthis.name.equals(otherSimpson.name)&& this.hashCode()==otherSimpson.hashCode(); } @Override publicinthashCode(){ return(43+777); } } }
Проанализируйте код, угадайте результат, а затем запустите программу. Ваша цель заключается в том, чтобы улучшить навыки анализа кода,усвоить основные концепции Java и сделать создаваемый код более эффективным. Выберите свой вариант, прежде чем проверять правильный ответ, который приведен ниже.
A) true true 4 B) true false 3 C) true false 2 D) false true 3