Почему мне нужно переопределить методы equals и hashCode в Java? - PullRequest
336 голосов
/ 15 февраля 2010

Недавно я прочитал это Документ разработчика работ .

Документ посвящен эффективному и правильному определению hashCode() и equals(), однако я не могу понять, почему мы должны переопределить эти два метода.

Как я могу принять решение об эффективной реализации этих методов?

Ответы [ 28 ]

2 голосов
/ 24 марта 2016

В приведенном ниже примере, если вы закомментируете переопределение для equals или hashcode в классе Person, этот код не сможет найти порядок Тома. Использование реализации хеш-кода по умолчанию может привести к сбоям в поисках хеш-таблиц.

Ниже я приведу упрощенный код, который устанавливает порядок людей. В качестве ключа в хеш-таблице используется человек.

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

    public Person(String name, int age, String socialSecurityNumber) {
        this.name = name;
        this.age = age;
        this.socialSecurityNumber = socialSecurityNumber;
    }

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}
1 голос
/ 19 апреля 2018

Чтобы помочь вам проверить дубликаты Объектов, нам нужен пользовательский equals и hashCode.

Поскольку хэш-код всегда возвращает число, всегда можно быстро получить объект, используя номер, а не алфавитный ключ. Как это будет происходить? Предположим, мы создали новый объект, передав некоторое значение, которое уже доступно в каком-то другом объекте. Теперь новый объект будет возвращать то же хеш-значение, что и для другого объекта, поскольку переданное значение такое же. Когда возвращается одно и то же значение хеш-функции, JVM будет каждый раз переходить на один и тот же адрес памяти, и если в случае наличия более одного объекта для одного и того же хеш-значения, он будет использовать метод equals () для определения правильного объекта.

1 голос
/ 10 октября 2014

hashCode() метод используется для получения уникального целого числа для данного объекта.Это целое число используется для определения местоположения сегмента, когда этот объект должен быть сохранен в некоторой HashTable, HashMap подобной структуре данных.По умолчанию метод Object hashCode() возвращает целочисленное представление адреса памяти, где хранится объект.

Метод объектов hashCode() используется, когда мы вставляем их в HashTable, HashMap или HashSet.Подробнее о HashTables на Wikipedia.org для справки.

Чтобы вставить любую запись в структуру данных карты, нам нужны ключ и значение.Если и ключ, и значения являются определяемыми пользователем типами данных, ключ hashCode() будет определять, где хранить объект внутри.Когда требуется поискать объект на карте, хеш-код ключа будет определять, где искать объект.

Хеш-код указывает только на определенную «область» (или список, область и т. Д.).) внутренне.Поскольку разные ключевые объекты потенциально могут иметь один и тот же хеш-код, сам хеш-код не является гарантией того, что правильный ключ найден.Затем HashTable итерирует эту область (все ключи с одинаковым хеш-кодом) и использует метод ключа equals(), чтобы найти правильный ключ.Как только правильный ключ найден, объект, сохраненный для этого ключа, возвращается.

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

ПРИМЕЧАНИЯ:

  1. Всегда использовать одинаковые атрибуты объекта для генерации hashCode() и equals() обоих.Как и в нашем случае, мы использовали идентификатор сотрудника.

  2. equals() должен быть непротиворечивым (если объекты не изменены, то он должен продолжать возвращать одно и то же значение).

  3. Всякий раз, когда a.equals(b), a.hashCode() должно совпадать с b.hashCode().

  4. Если вы переопределяете одно, то вы должны переопределить другое.

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html

1 голос
/ 05 июня 2017

ИМХО, это согласно правилу гласит: если два объекта равны, то они должны иметь одинаковый хеш, то есть равные объекты должны давать равные значения хеш-функции.

Учитывая выше, по умолчанию equals () в Object is ==, который выполняет сравнение по адресу, hashCode () возвращает адрес в целочисленном виде (хэш по фактическому адресу), который снова различен для отдельного объекта.

Если вам нужно использовать пользовательские объекты в коллекциях на основе хэша, вам нужно переопределить как equals (), так и hashCode (), например, если я хочу сохранить HashSet объектов Employee, если я не использую более сильный hashCode и equals Я могу в конечном итоге переопределить два разных объекта Employee, это происходит, когда я использую age в качестве hashCode (), однако я должен использовать уникальное значение, которое может быть идентификатором Employee ID.

0 голосов
/ 17 июля 2018
public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

Тестовый класс

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

В Object Class equals (Object obj) используется для сравнения сравнения адресов, поэтому, когда в классе Test вы сравниваете два объекта, метод equals дает false, но когда мы переопределяем hashcode (), он может сравнивать содержимое и давать правильный результат.

0 голосов
/ 16 июля 2018

Если вы хотите сохранить и извлечь свой пользовательский объект в качестве ключа на карте, то вы всегда должны переопределять equals и hashCode в своем пользовательском объекте. Например:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

Здесь p1 & p2 будут рассматривать только как один объект, а map size будет только 1, потому что они равны.

0 голосов
/ 19 февраля 2016

Bah - «Вы должны переопределить hashCode () в каждом классе, который переопределяет equals ().»

[из Эффективной Явы, Джошуа Блох?]

Разве это не неправильно? Переопределение hashCode, вероятно, подразумевает, что вы пишете класс ключа хеша, но переопределение equals определенно этого не делает. Есть много классов, которые не используются в качестве хеш-ключей, но по какой-то другой причине хотят метод тестирования на логическое равенство. Если вы выберете «равно» для него, вам может быть поручено написать реализацию hashCode из-за чрезмерного усердия в применении этого правила. Все, что достигается, - это добавить непроверенный код в кодовую базу, злое ожидание, чтобы запутать кого-то в будущем. Также написание кода, который вам не нужен, является анти-проворным. Это просто неправильно (и созданный идеей, вероятно, будет несовместим с вашими созданными вручную равными).

Конечно, они должны были назначить интерфейс для объектов, написанных для использования в качестве ключей? В любом случае, Object никогда не должен был предоставлять hashCode () и equals () по умолчанию imho. Вероятно, это поощряется многими битыми коллекциями хешей.

Но в любом случае, я думаю, что "правило" написано задом наперед. А пока я буду избегать использования «равно» для методов проверки на равенство: - (

0 голосов
/ 15 февраля 2010

Оба метода определены в классе Object. И оба в своей простейшей реализации. Поэтому, когда вам нужно, вы захотите добавить еще реализацию к этим методам, тогда у вас есть переопределение в вашем классе.

Для Ex: метод equals () в объекте проверяет его равенство только по ссылке. Поэтому, если вам нужно сравнить его состояние, вы можете переопределить его, как это делается в классе String.

...