Зачем переопределять equals и hashcode одновременно
Перейти к содержимому

Зачем переопределять equals и hashcode одновременно

  • автор:

Создание переопределений для методов Equals и GetHashCode в Visual Studio

Что? Эта возможность позволяет создавать методы Equals и GetHashCode.

Когда? Создавайте эти переопределения, если у вас есть тип, который нужно сравнить по одному или нескольким полям, а не по расположению объектов в памяти.

Зачем?

  • При реализации типа значения рекомендуется переопределить метод Equals. При этом обеспечивается повышенная производительность по сравнению с реализацией метода Equals по умолчанию для ValueType.
  • При реализации ссылочного типа рекомендуется переопределить метод Equals, если ваш тип выглядит как базовый, например Point, String, BigNumber и т. д.
  • Переопределите метод GetHashCode, чтобы тип правильно работал в хэш-таблице. Дополнительные сведения см. в руководстве по операторам равенства.

Практические советы

  1. Поместите курсор в любую позицию на строке объявления типа.
public class ImaginaryNumber < public double RealNumber < get; set; >public double ImaginaryUnit < get; set; >> 

Screenshot of highlighted code on which to apply the generated method

Теперь код должен выглядеть как на следующем снимке экрана:

Совет Не выбирайте имя типа двойным щелчком, иначе параметр меню будет недоступен. Просто установите курсор в любую позицию на строке.

  • Далее выберите одно из следующих действий:
    • Нажмите CTRL+., чтобы открыть меню Быстрые действия и рефакторинг.
    • Щелкните правой кнопкой мыши и выберите меню Быстрые действия и рефакторинг.
    • Screenshot of the Quick Actions screwdriver icon in Visual StudioЩелкните значок, который отображается в левом поле.
  • В раскрывающемся меню выберите Создать Equals(object) или Создать Equals и GetHashCode. Screenshot of the Generate Overrides drop-down menu
  • В диалоговом окне Выбрать члены выберите члены, для которых хотите создать методы: Generate overrides dialog

    Совет В этом диалоговом окне также можно создавать операторы, используя флажок в нижней части окна.
    Методы Equals и GetHashCode создаются с реализациями по умолчанию, как показано в следующем коде:

    public class ImaginaryNumber : IEquatable  < public double RealNumber < get; set; >public double ImaginaryUnit < get; set; >public override bool Equals(object obj) < return Equals(obj as ImaginaryNumber); >public bool Equals(ImaginaryNumber other) < return other != null && RealNumber == other.RealNumber && ImaginaryUnit == other.ImaginaryUnit; >public override int GetHashCode() < return HashCode.Combine(RealNumber, ImaginaryUnit); >> 

    Screenshot of the result of the generated method

    Теперь код должен выглядеть как на следующем снимке экрана:

    См. также

    • Создание кода
    • Просмотр изменений

    Зачем переопределять методы Equals() и GetHashCode() в C#

    Можете объяснить, зачем в коде переопределять данные методы в коде. И при этом почему то пишут что они должны переопределяться вместе. Вот даже просто взял пример. зачем тут это, почему без этого переопределения не работает. Как оно все устроено.

    Person[] students = < new Person("Tom"), new Person("Bob"), new Person("Sam") >; Person[] employees = < new Person("Tom"), new Person("Bob"), new Person("Mike") >; // объединение последовательностей var people = students.Union(employees); foreach (Person person in people) Console.WriteLine(person.Name); class Person < public string Name < get;>public Person(string name) => Name = name; public override bool Equals(object? obj) < if (obj is Person person) return Name == person.Name; return false; >public override int GetHashCode() => Name.GetHashCode(); > 

    Отслеживать
    задан 23 окт 2022 в 10:36
    1 1 1 бронзовый знак
    А в книге по C# этот момент не объясняется?
    23 окт 2022 в 10:42
    23 окт 2022 в 10:55

    Ну, что-то будет работать. Но как только вы захотите использовать ваши объекты в словаре в качестве ключей или в хэшсете, тут то всё это и понадобится. Ну и если вы объекты просто где-то захотите сравнивать, то Equals само по себе понадобится.

    23 окт 2022 в 11:01

    Автор, а вы представьте себя на месте метода Union. Вот вам дали на вход два набора объектов, и вам надо выдать на выход один набор без повторов. Как вы будете решать эту задачу без возможности сравнить объекты?

    23 окт 2022 в 11:16

    2 ответа 2

    Сортировка: Сброс на вариант по умолчанию

    Equals переопределяется, чтобы сравнивать объекты. К примеру, у тебя 2 объекта одного типа, и они могут быть равны или одинаковы по каким-то условиям, общая сумма всех внутренних переменных одинакова или полностью идентичны, но быть независимыми друг от друга. Для этого и существует метод Equals, который для других пользователей твоего куска кода, позволяет сравнивать объекты. Можно так-же этому методы дать перегрузки, чтоб он принимал не только «object? obj», но и «int» и прочие типы объектов, но там уже переопределений не будет.

    GetHashCode используется для ускорения сравнения двух объектов. То есть если требуется узнать, одинаковы ли какие-то два объекта, то сначала сравниваются их хэш-коды. Если они различаются, то значит и объекты различны. Если же совпадают, то тогда начинается дорогостоящее «настоящее» сравнение через Equals. Таким образом, GetHashCode, во-первых, должен совпадать для одинаковых объектов, во-вторых, по-возможности отличаться для разных объектов и в-третьих, достаточно быстро вычисляться (чтобы в его использовании вообще был смысл). В вашем случае вряд ли преобразование целых в строку и затем их сравнение будет выполняться быстрее чем сравнение чисел напрямую.

    Отслеживать
    ответ дан 23 окт 2022 в 11:07
    47 6 6 бронзовых знаков

    В том-то и дело, что может не объясняться. Часто базовые вещи опускают. Для «GetHashCode()» в документации https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=net-6.0 написано: «A hash code is intended for efficient insertion and lookup in collections that are based on a hash table». Это значит, что если использовать класс в качестве ключа коллекции не планируется, то можно и не переопределять. В качестве ключа чаще всего используются какие-то базовые типы вроде int или string, для которых вычисление хеша уже реализовано. «Equals()» используется в паре с «GetHashCode()».

    То же самое для «==». Метод позволяет сравнивать объекты с помощью операции равенства. Если такие сравнения не планируются, то можно не переопределять. (Сделал уточнение согласно комментарию.)

    Интересно, что переопределение «==» требует переопределения «Equals()», но не наоборот.

    Отслеживать
    ответ дан 23 окт 2022 в 11:09
    3,994 2 2 золотых знака 3 3 серебряных знака 18 18 бронзовых знаков

    == не будет работать, если вы просто переопределите эти методы. Для их работы нужно переопределять сами операторы дополнительно.

    Почему в Java нужно переопределять методы equals и hashCode?

    В программировании на Java часто возникают ситуации, когда необходимо сравнивать объекты на равенство. Для этого в Java есть два метода: equals() и hashCode() . Они являются методами класса Object , от которого наследуются все классы в Java.

    Пример

    Возьмем простой пример. У нас есть класс Person с двумя полями: name и age .

    public class Person < private String name; private int age; public Person(String name, int age) < this.name = name; this.age = age; >>

    Попробуем сравнить два объекта этого класса:

    Person person1 = new Person("John", 25); Person person2 = new Person("John", 25); System.out.println(person1.equals(person2)); // false

    Здесь ожидается, что два объекта будут равны, так как их имя и возраст совпадают. Однако, метод equals() по умолчанию сравнивает ссылки на объекты, а не их содержимое.

    Переопределение метода equals

    Чтобы корректно сравнивать объекты по содержимому, необходимо переопределить метод equals() . Это позволяет установить свои правила сравнения объектов.

    @Override public boolean equals(Object obj)

    Теперь метод equals() сравнивает объекты по содержимому:

    Person person1 = new Person("John", 25); Person person2 = new Person("John", 25); System.out.println(person1.equals(person2)); // true

    Переопределение метода hashCode

    С другой стороны, метод hashCode() важен для корректной работы некоторых структур данных, таких как HashSet , HashMap и т.д. Эти структуры используют хеш-код объекта для быстрого доступа к нему. Если два объекта равны по методу equals() , то их хеш-коды должны быть равны.

    @Override public int hashCode()

    В итоге, переопределение методов equals() и hashCode() позволяет корректно сравнивать объекты и использовать их в коллекциях. Если эти методы не переопределены, могут возникнуть проблемы сравнения объектов и работой коллекций.

    Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java?

    Java-университет

    Зачем переопределять методы equals и hashcode в Java?

    Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 1

    Источник: Medium Содержание этой статьи посвящено двум тесно связанным между собой методам: equals() и hashcode() . Вы узнаете, как они взаимодействуют друг с другом и как их правильно переопределять.

    Почему мы переопределяем метод equals()?

    В Java мы не можем перегружать поведение таких операторов, как == , += , -+ . Они работают согласно заданному процессу. Для примера рассмотрим работу оператора == .

    Как работает оператор ==?

    Он проверяет, указывают ли две сравниваемые ссылки на один и тот же экземпляр в памяти. Оператор == будет иметь значение true только в том случае, если эти две ссылки представляют один и тот же экземпляр в памяти. Давайте взглянем на пример кода:

     public class Person

    Допустим, в вашей программе вы создали два объекта Person в разных местах и ​​хотите их сравнить.

     Person person1 = new Person("Mike", 34); Person person2 = new Person("Mike", 34); System.out.println( person1 == person2 ); --> will print false! 

    С точки зрения бизнеса эти два объекта выглядят одинаково, верно? Но для JVM они не совпадают. Поскольку они оба созданы с помощью ключевого слова new , эти экземпляры расположены в разных сегментах памяти. Поэтому оператор == вернет false. Но если мы не можем переопределить оператор == , то как нам сказать JVM, что мы хотим, чтобы эти два объекта обрабатывались одинаково? Здесь в игру вступает метод .equals() . Вы можете переопределить equals() , чтобы проверить, имеют ли некоторые объекты одинаковые значения для определенных полей, чтобы считать их равными. Вы можете выбрать, какие поля нужно сравнить. Если мы говорим, что два объекта Person будут одинаковыми только тогда, когда они имеют одинаковый возраст и одно и то же имя, то в этом случае IDE сгенерирует для автоматического создания equals() что-то такое:

     @Override public boolean equals(Object o)

    Вернемся к нашему предыдущему примеру.

     Person person1 = new Person("Mike", 34); Person person2 = new Person("Mike", 34); System.out.println ( person1 == person2 ); --> will print false! System.out.println ( person1.equals(person2) ); --> will print true! 

    Да, мы не можем перегрузить оператор == для сравнения объектов так, как мы хотим, но Java дает нам другой способ — метод equals() , который мы можем переопределить по своему усмотрению. Имейте в виду, что если мы не предоставим нашу пользовательскую версию .equals() (также известную как переопределение) в нашем классе, то предопределенный .equals() из класса Object и оператор == будут вести себя одинаково. Метод по умолчанию equals() , унаследованный от Object , будет проверять, совпадают ли оба сравниваемых экземпляра в памяти!

    Почему мы переопределяем метод hashCode()?

    Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 2

    Некоторые структуры данных в Java, такие как HashSet и HashMap , хранят свои элементы на основе хеш-функции, которая применяется к этим элементам. Хеш-функцией является hashCode() . Если у нас есть выбор в переопределении метода .equals() , то у нас также должен быть выбор в переопределении метода hashCode() . Для этого есть причина. Ведь реализация по умолчанию hashCode() , унаследованная от Object , считает все объекты в памяти уникальными! Но вернемся к этим структурам хеш-данных. Для этих структур данных существует правило. HashSet не может содержать повторяющиеся значения, а HashMap не может содержать повторяющиеся ключи. HashSet реализован с помощью HashMap таким образом, что каждое значение HashSet хранится как ключ в HashMap . Как работает HashMap ? HashMap — это собственный массив с несколькими сегментами. Каждый сегмент имеет связанный список ( linkedList ). В этом связанном списке хранятся наши ключи. HashMap находит правильный linkedList для каждого ключа, применяя метод hashCode() , а затем выполняет итерацию по всем элементам этого linkedList и применяет метод equals() к каждому из этих элементов, чтобы проверить, содержится ли там этот элемент. Дубликаты ключей не допускаются. Когда мы помещаем что-то внутрь HashMap , то ключ сохраняется в одном из этих связанных списков. В каком связанном списке будет храниться этот ключ, показывает результат метода hashCode() для этого ключа. То есть, если key1.hashCode() в результате получается 4, то этот key1 будет храниться в 4-м сегменте массива в существующем там LinkedList . По умолчанию метод hashCode() возвращает разные результаты для каждого экземпляра. Если у нас есть значение по умолчанию equals() , которое ведет себя как == , рассматривая все экземпляры в памяти как разные объекты, то проблем не будет. Как вы помните, в нашем предыдущем примере было сказано, что мы хотим, чтобы экземпляры Person считались равными, если их возраст и имена совпадают.

     Person person1 = new Person("Mike", 34); Person person2 = new Person("Mike", 34); System.out.println ( person1.equals(person2) ); --> will print true! 

    Теперь давайте создадим карту (map) для хранения этих экземпляров в виде ключей с определенной строкой в ​​качестве парного значения.

     Map map = new HashMap(); map.put(person1, "1"); map.put(person2, "2"); 

    Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 3

    В классе Person мы не переопределили метод hashCode , но у нас есть переопределенный метод equals . Поскольку значение по умолчанию hashCode дает разные результаты для разных Java-экземпляров person1.hashCode() и person2.hashCode() , есть большие шансы получить разные результаты. Наша карта может заканчиваться разными person в разных связанных списках. Это противоречит логике HashMap . Ведь HashMap не может иметь несколько одинаковых ключей! Дело в том, что по умолчанию hashCode() , унаследованного от класса Object , недостаточно. Даже после того, как мы переопределили метод equals() класса Person . Вот почему мы должны переопределить метод hashCode() после того, как мы переопределили метод equals . Теперь давайте это исправим. Нам нужно переопределить наш метод hashCode() , чтобы он учитывал те же поля, что и equals() , а именно age и name .

     public class Person < private Integer age; private String name; ..getters, setters, constructors @Override public boolean equals(Object o) < if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && name.equals(person.name); >@Override public int hashCode()

    В методе hashCode() мы использовали простое значение (вы можете использовать любые другие значения). Тем не менее, предлагается использовать простые числа, чтобы создавать меньше проблем. Давайте попробуем еще раз сохранить эти ключи в нашем HashMap :

     Map map = new HashMap(); map.put(person1, "1"); map.put(person2, "2"); 

    Кофе-брейк #168. Зачем переопределять методы equals и hashcode в Java? - 4

    person1.hashCode() и person2.hashCode() будут одинаковы. Допустим, равны 0. HashMap перейдет в сегмент 0 и в нем LinkedList сохранит person1 как ключ со значением “1”. Во втором случае, когда HashMap снова перейдет к корзине 0, чтобы сохранить ключ person2 со значением “2”, он увидит, что там уже существует другой равный ему ключ. Таким образом он перезапишет предыдущий ключ. И в нашем HashMap будет существовать только ключ person2 . Так мы узнали, как работает правило HashMap , которое гласит, что нельзя использовать несколько одинаковых ключей! Однако имейте в виду, что неравные экземпляры могут иметь одинаковый хэшкод, а одинаковые экземпляры должны возвращать одинаковый хэшкод.

  • Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *