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

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

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

Как я могу принять решение эффективно внедрить эти методы?

Ответ 1

Джошуа Блох говорит об эффективной Java

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

Попробуйте понять это с примером того, что произойдет, если мы переопределим equals() без переопределения hashCode() и попытаемся использовать Map.

Скажем, у нас есть класс, подобный этому, и что два объекта MyClass равны, если их importantField равно (с hashCode() и equals(), сгенерированными eclipse)

public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

Только переопределение equals

Если только equals переопределяется, тогда, когда вы вызываете myMap.put(first,someValue), сначала будет хеш для некоторого ведра, а когда вы вызываете myMap.put(second,someOtherValue), он будет хешировать в каком-либо другом ведре (поскольку у них есть другой hashCode). Итак, хотя они равны, поскольку они не имеют хэша в том же ведре, карта не может реализовать его, и оба они остаются на карте.


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

Только переопределение hashCode

Представьте, что у вас есть это

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Если вы только переопределяете hashCode, то при вызове myMap.put(first,someValue) он берет сначала, вычисляет его hashCode и сохраняет его в данном ковше. Затем, когда вы вызываете myMap.put(second,someOtherValue), он должен сначала заменить второй на Map Documentation, потому что они равны (согласно бизнес-требованиям).

Но проблема в том, что equals не было переопределено, поэтому, когда хэши карты second и повторяются через ведро, глядя, есть ли объект k такой, что second.equals(k) истинно, он не найдет никого как second.equals(first) будет false.

Надеюсь, что было ясно.

Ответ 2

Коллекции, такие как HashMap и HashSet используют значение хэш-кода объекта, чтобы определить, как он должен храниться в коллекции, а хэш-код используется снова, чтобы найти объект в его коллекции.

Хеширование поиска состоит из двух этапов:

  1. Найдите правильное ведро (используя hashCode())
  2. Найдите в корзине нужный элемент (используя equals())

Вот небольшой пример того, почему мы должны переопределить equals() и hashcode().

Рассмотрим класс Employee с двумя полями: возраст и имя.

public class Employee {

    String name;
    int age;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

Теперь создайте класс, вставьте объект Employee в HashSet и проверьте, присутствует ли этот объект или нет.

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

Будет напечатано следующее:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

Теперь раскомментируйте метод hashcode(), выполните то же самое, и результат будет:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

Теперь вы можете понять, почему, если два объекта считаются равными, их хэш-коды также должны быть равны? В противном случае вы никогда не сможете найти объект, поскольку метод хеш-кода по умолчанию в классе Object практически всегда предлагает уникальный номер для каждого объекта, даже если метод equals() переопределяется таким образом, что два или более объекта считаются равными. Неважно, насколько равны объекты, если их хеш-коды не отражают это. Итак, еще раз: если два объекта равны, их хэш-коды также должны быть равны.

Ответ 3

Вы должны переопределить hashCode() в каждом класс, который переопределяет equals(). недостаточность это приведет к нарушению общий контракт на Object.hashCode(), который предотвратит ваш класс работает правильно в сочетании со всеми хэш-настройками коллекции, включая HashMap, HashSet и Hashtable.


  из эффективной Java, Джошуа Блох

Последовательно определяя equals() и hashCode(), вы можете улучшить удобство использования ваших классов в качестве ключей в коллекциях на основе хэшей. Поскольку API-документ для hashCode объясняет: "Этот метод поддерживается в интересах хэш-таблиц, таких как те, которые предоставляются java.util.Hashtable".

Лучший ответ на ваш вопрос о том, как эффективно реализовать эти методы, предлагает вам прочитать главу 3 Эффективная Java.

Ответ 4

Проще говоря, метод equals в Object проверяет ссылочное равенство, где, поскольку два экземпляра вашего класса могут по-прежнему быть семантически равными, когда свойства равны. Это, например, важно при помещении ваших объектов в контейнер, который использует equals и hashcode, например HashMap и Set. Скажем, у нас есть класс вроде:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

Мы создаем два экземпляра с тем же id:

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

Без превышения равных мы получаем:

  • a.equals(b) ложно, потому что это два разных экземпляра
  • a.equals(a) истинно, так как он тот же экземпляр
  • b.equals(b) истинно, так как он тот же экземпляр

Правильно? Возможно, если это то, чего вы хотите. Но предположим, что мы хотим, чтобы объекты с одним и тем же идентификатором были одним и тем же объектом, независимо от того, были ли они двумя разными экземплярами. Мы переопределяем equals (и hashcode):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

Что касается реализации equals и hashcode, я могу порекомендовать использовать методы поддержки Guava

Ответ 5

Идентичность - это не равенство.

  • равен operator == test identity.
  • equals(Object obj) сравнивает тест равенства (т.е. нам нужно сказать равенство, переопределяя метод)

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

Сначала мы должны понимать использование метода equals.

Чтобы идентифицировать различия между двумя объектами, нам нужно переопределить метод equals.

Например:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

Теперь метод hashCode может легко понять.

hashCode создает целое число для хранения объекта в структурах данных, таких как HashMap, HashSet.

Предположим, что мы переопределили метод Customer как указано выше,

customer1.equals(customer2);  // returns true by our own logic

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

  • не равные экземпляры могут иметь один и тот же хэш-код.
  • равные экземпляры должны возвращать одинаковый хэш-код.

Ответ 6

Хорошо, позвольте мне объяснить концепцию очень простыми словами.

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

Чтобы понять, почему мы должны переопределять оба метода equals и hashcode, если вам нужно сначала понять, что такое hashmap и что делает.

Хешмап - это структура данных, в которой хранятся пары значений ключа в массиве. Давайте скажем a [], где каждый элемент в 'a' является парой значений ключа.

Также каждый индекс в указанном выше массиве может быть связанным списком, тем самым имея более одного значения при одном индексе.

Теперь почему используется хэшмап? Если нам нужно искать среди большого массива, то поиск по каждому из них, если они не будут эффективными, так что хэш-метод говорит нам, что позволяет предварительно обрабатывать массив с некоторой логикой и группировать элементы на основе этой логики, то есть Хеширование

например: у нас есть массив 1,2,3,4,5,6,7,8,9,10,11, и мы применяем хеш-функцию mod 10, поэтому 1,11 будут сгруппированы вместе. Поэтому, если бы нам пришлось искать 11 в предыдущем массиве, нам пришлось бы перебирать полный массив, но когда мы группируем его, мы ограничиваем объем итераций, тем самым улучшая скорость. Эта структура данных, используемая для хранения всей вышеприведенной информации, может рассматриваться как массив 2d для простоты

Теперь помимо вышеизложенного hashmap также говорит, что он не будет добавлять в него какие-либо дубликаты. И это главная причина, по которой мы должны переопределять equals и hashcode

Итак, когда его сказано, что объясняют внутреннюю работу hashmap, нам нужно найти, какие методы имеет hashmap, и как он соответствует приведенным выше правилам, которые я объяснил выше.

поэтому в hashmap есть метод, называемый put (K, V), и в соответствии с hashmap он должен следовать приведенным выше правилам эффективного распределения массива и не добавлять никаких дубликатов

так что put put это то, что он сначала сгенерирует хэш-код для данного ключа, чтобы решить, какой индекс должен иметь значение. Если в этом индексе нет ничего нового, тогда новое значение будет добавлено там, если что-то уже присутствует там, тогда новое значение должно быть добавлено после окончания связанного списка по этому индексу. но не помните, что дубликаты не должны добавляться в соответствии с желаемым поведением хэш-карты. поэтому давайте предположим, что у вас есть два объекта Integer aa = 11, bb = 11. как каждый объект, полученный из класса объекта, реализация по умолчанию для сравнения двух объектов заключается в том, что она сравнивает ссылку, а не значения внутри объекта. Таким образом, в приведенном выше случае оба семантически равных будут терпеть неудачу в тесте равенства и возможность того, что будут существовать два объекта, один и тот же хэш-код и одинаковые значения, что создает дубликаты. Если мы переопределим, мы могли бы избежать добавления дубликатов. Вы также можете обратиться к Подробности работы

import java.util.HashMap;


public class Employee {

String name;
String mobile;
public Employee(String name,String mobile) {
    this.name=name;
    this.mobile=mobile;
}

@Override
public int hashCode() {
    System.out.println("calling hascode method of Employee");
    String str=this.name;
    Integer sum=0;
    for(int i=0;i<str.length();i++){
        sum=sum+str.charAt(i);
    }
    return sum;

}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    System.out.println("calling equals method of Employee");
    Employee emp=(Employee)obj;
    if(this.mobile.equalsIgnoreCase(emp.mobile)){

        System.out.println("returning true");
        return true;
    }else{
        System.out.println("returning false");
        return false;
    }


}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    Employee emp=new Employee("abc", "hhh");
    Employee emp2=new Employee("abc", "hhh");
    HashMap<Employee, Employee> h=new HashMap<>();
    //for (int i=0;i<5;i++){
        h.put(emp, emp);
        h.put(emp2, emp2);

    //}

    System.out.println("----------------");
    System.out.println("size of hashmap: "+h.size());


}

}

Ответ 7

hashCode():

Если вы только переопределите метод хэш-кода, ничего не произойдет. Потому что он всегда возвращает новый hashCode для каждого объекта как класс Object.

equals():

Если вы только переопределяете равный метод, a.equals(b) истинно, значит, hashCode для a и b должен быть таким же, но не произойти. Поскольку вы не переопределили метод hashCode.

Примечание: метод hashCode() класса Object всегда возвращает новый hashCode для каждого объекта.

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

Ответ 8

Java ставит правило, которое

"Если два объекта равны при использовании метода Object класса equals, то метод хэш-кода должен дать одинаковое значение для этих двух объектов".

Итак, если в нашем классе мы переопределяем equals() мы должны переопределить метод hashcode() также, чтобы следовать этому правилу. Оба метода, equals() и hashcode(), используются в Hashtable, например, для хранения значений в виде пар ключ-значение. Если мы переопределим одно, а не другое, существует вероятность того, что Hashtable может работать не так, как мы хотим, если мы используем такой объект в качестве ключа.

Ответ 9

Потому что, если вы не переопределите их, вы будете использовать имплантацию по умолчанию в Object.

Учитывая, что значения равенства и значения enccode обычно требуют знания того, что составляет объект, они, как правило, должны быть переопределены в вашем классе, чтобы иметь какой-либо ощутимый смысл.

Ответ 10

Чтобы использовать наши собственные объекты класса как ключи в таких коллекциях, как HashMap, Hashtable и т.д., мы должны переопределить оба метода (hashCode() и equals()), узнав о внутренней работе коллекции. В противном случае это приводит к неправильным результатам, которых мы не ожидаем.

Ответ 11

Добавление в ответ @Lombo

Когда вам нужно переопределить equals()?

Стандартная реализация Object equals() - это

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

что означает, что два объекта будут считаться равными, только если они имеют одинаковый адрес памяти, который будет истинным, только если вы сравнивая объект с самим собой.

Но вы можете рассмотреть два объекта одинаково, если они имеют одинаковое значение для одного или более их свойств (см. пример, приведенный в ответе @Lombo).

Таким образом, вы переопределите equals() в этих ситуациях, и вы дадите свои собственные условия для равенства.

Я успешно применил equals(), и он отлично работает. Поэтому почему они также требуют переопределить hashCode()?

Хорошо. Пока вы не используете коллекции "Hash" на вашем пользовательском классе, это нормально. Но в будущем вы, возможно, захотите использовать HashMap или HashSet, и если вы не override и "правильно реализуете" hashCode(), эта коллекция на основе Hash не будет работать так, как предполагалось.

Переопределить только равные (добавление к ответу @Lombo)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

Прежде всего, HashMap проверяет, является ли хэш-код second тем же, что и first. Только если значения совпадают, он будет проверять равенство в том же ведре.

Но здесь hashCode отличается для этих 2 объектов (потому что у них есть другой адрес памяти - от реализации по умолчанию). Следовательно, даже не стоит проверять равенство.

Если у вас есть точка останова внутри вашего переопределенного метода equals(), он не будет входить, если у них разные хэш-коды. contains() проверяет hashCode() и только если они одинаковы, он будет называть ваш метод equals().

Почему мы не можем сделать проверку HashMap равным во всех ведрах? Поэтому мне не нужно переопределять hashCode()!!

Тогда вам не хватает точки из Hash based Collections. Рассмотрим следующее:

Your hashCode() implementation : intObject%9.

Ниже приведены ключи, хранящиеся в форме ведер.

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

Скажите, вы хотите знать, содержит ли карта ключ 10. Вы хотите найти все ведра? или Вы хотите искать только одно ведро?

На основе hashCode вы должны определить, что если 10 присутствует, оно должно присутствовать в ведре 1. Таким образом, будет искать только Bucket 1!

Ответ 12

class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}
  • put ('key', 'value') будет вычислять значение хэша с помощью hashCode(), чтобы определить bucket и использует метод equals() для определения того, уже ли это значение присутствующих в ковше. Если это не будет добавлено, то оно будет заменено текущим значением
  • get ('key') будет использовать hashCode(), чтобы сначала найти запись (ведро) и equals(), чтобы найти значение в Entry

если оба параметра переопределены,

Карта < A >

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

если equals не переопределяется

Карта < A >

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

Если hashCode не переопределяется

Карта < A >

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

Равный контракт HashCode

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

Ответ 13

Это полезно при использовании объектов значения. Ниже приводится выдержка из репозитория шаблонов Portland:

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

Поэтому у меня может быть несколько копий объекта, представляющего дату 16 января 1998 года. Любая из этих копий будет равна друг другу. Для небольших объектов, таких как этот, часто проще создавать новые и перемещать их, чем полагаться на один объект для представления даты.

Объект значения всегда должен переопределять .equals() в Java (или = в Smalltalk). (Не забудьте переопределить .hashCode().)

Ответ 14

Я искал объяснение: "Если вы только переопределяете hashCode, тогда, когда вы вызываете myMap.put(first,someValue), сначала берется, вычисляет свой хэш-код и сохраняет его в данном ковше. Тогда, когда вы вызываете myMap.put(first,someOtherValue), он должен сначала заменить вторым согласно Документации на карте, потому что они равны (согласно нашему определению)".

Я думаю, что 2-й раз, когда мы добавляем в myMap, тогда он должен быть "вторым" объектом, например myMap.put(second,someOtherValue)

Ответ 15

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

Для тенниса - желтый, красный. Для крикета - белый

Теперь ведро имеет шары в трех цветах: желтый, красный и белый. И что теперь вы сделали раскраску Только вы знаете, какой цвет для какой игры.

Раскрашивание шаров - Хеширование. Выбор мяча для игры - Равно.

Если вы сделали раскраску, а кто-то выбрал мяч для крикета или тенниса, они не будут помнить цвет.

Ответ 16

1) Общая ошибка показана в приведенном ниже примере.

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

зеленый автомобиль не найден

2. Проблема, вызванная hashCode()

Проблема вызвана неперекрываемым методом hashCode(). Контракт между equals() и hashCode() равен:

  • Если два объекта равны, то они должны иметь один и тот же хэш-код.
  • Если два объекта имеют один и тот же хэш-код, они могут быть или не быть равными.

    public int hashCode(){  
      return this.color.hashCode(); 
    }
    

Ответ 17

Предположим, что у вас есть класс (A), который объединяет два других (B) (C), и вам нужно хранить экземпляры (A) внутри хэш-таблицы. Реализация по умолчанию позволяет различать экземпляры, но не (B) и (C). Таким образом, два экземпляра A могут быть равны, но по умолчанию вы не сможете сравнить их правильно.

Ответ 18

Методы equals и hashcode определены в классе объектов. По умолчанию, если метод equals возвращает true, система будет идти дальше и проверять значение хэш-кода. Если хэш-код из двух объектов также одинаковый, тогда объекты будут считаться одинаковыми. Поэтому, если вы переопределяете только метод equals, то, хотя метод overridden equals указывает, что 2 объекта равны, системный хэш-код может не указывать на то, что 2 объекта равны. Поэтому нам нужно переопределить хэш-код.

Ответ 19

Методы Equals и Hashcode в Java

Это методы класса java.lang.Object, который является суперклассом всех классов (пользовательских классов и других, определенных в java API).

Реализация:

public boolean equals (Object obj)

public int hashCode()

введите описание изображения здесь

public boolean equals (Object obj)

Этот метод просто проверяет, соответствуют ли две ссылки на объекты x и y одному и тому же объекту. т.е. проверяет, если x == y.

Это рефлексивное:. Для любого опорного значения х, x.equals(х) должен возвращать истинное

Он симметричен: для любых опорных значений x и y x.equals(y) должен возвращать true тогда и только тогда, когда y.equals(x) возвращает true.

Это транзитивно: для любых ссылочных значений x, y и z, если x.equals(y) возвращает true, а y.equals(z) возвращает true, то x.equals(z) должно возвращать true.

Это согласуется: для любых ссылочных значений x и y множественные вызовы x.equals(y) последовательно возвращают true или последовательно возвращают false, если никакая информация, используемая при равных сравнениях с объектом, не изменяется.

Для любого ненулевого опорного значения х, x.equals(NULL) должен возвращать ложь.

public int hashCode()

Этот метод возвращает значение хэш-кода для объекта, для которого этот метод вызывается. Этот метод возвращает значение хэш-кода в виде целого числа и поддерживается в интересах хэш-хэш-классов, таких как Hashtable, HashMap, HashSet и т.д. Этот метод должен быть переопределен в каждом классе, который переопределяет метод equals.

Общий контракт hashCode:

Всякий раз, когда он вызывается одним и тем же объектом более одного раза во время выполнения приложения Java, метод hashCode должен последовательно возвращать одно и то же целое число, если информация, используемая при равных сравнениях с объектом, не изменяется.

Это целое число не должно оставаться согласованным с одним исполнением приложения на другое выполнение того же приложения.

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

Не требуется, чтобы, если два объекта неравны в соответствии с методом equals (java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен производить различные целочисленные результаты. Тем не менее, программист должен знать, что получение отдельных целочисленных результатов для неравных объектов может улучшить производительность хеш-таблиц.

Равные объекты должны создавать одинаковый хеш-код, если они равный, однако неравные объекты не должны создавать четкие хэш-коды.

Ресурсы:

JavaRanch

Изображение

Ответ 20

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

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

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

Ответ 21

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

Это приводит к проблеме, когда вы используете коллекцию Map, а ключ имеет тип Persistent, тип StringBuffer/builder. Поскольку они не переопределяют equals() и hashCode() в отличие от класса String, equals() возвращает false, когда вы сравниваете два разных объекта, хотя оба имеют одинаковое содержимое. Он заставит hashMap хранить одинаковые ключи содержимого. Сохранение одинаковых ключей содержимого означает, что оно нарушает правило Карты, поскольку Map не позволяет дублировать ключи вообще. Поэтому вы переопределяете методы equals(), а также методы hashCode() в своем классе и предоставляете реализацию (IDE может генерировать эти методы), чтобы они работали так же, как String equals() и hashCode() и предотвращали такие же ключи содержимого.

Вы должны переопределить метод hashCode() вместе с equals(), потому что equals() работает по hashcode.

Кроме того, переопределяющий метод hashCode() вместе с equals() помогает иннактировать контракт equals() - hashCode(): "Если два объекта равны, то они должны иметь одинаковый хеш-код".

Когда вам нужно написать собственную реализацию для hashCode()?

Как мы знаем, внутренняя работа HashMap основана на принципе Hashing. Существуют определенные ведра, в которых хранятся записи. Вы настраиваете реализацию hashCode() согласно вашему требованию, чтобы объекты той же категории могли храниться в одном индексе. при сохранении значений в коллекции карт с использованием метода put(k,v) внутренняя реализация put():

put(k, v){
hash(k);
index=hash & (n-1);
}

Значит, он генерирует индекс, и индекс генерируется на основе хэш-кода конкретного ключевого объекта. Поэтому сделайте этот метод генерирующим hashcode согласно вашему требованию, потому что те же записи hashcode будут сохранены в одном и том же ведре или индексе.

Что это!

Ответ 22

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

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

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

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

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

ПРИМЕЧАНИЯ:

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

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

  • Всякий раз, когда a.equals(b), тогда a.hashCode() должен быть таким же, как b.hashCode().

  • Если вы переопределите один, тогда вы должны переопределить другой.

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

Ответ 23

Причина этого: Когда ваши поля объекта могут быть пустыми, реализация Object.equals может быть болью, потому что вам нужно отдельно проверить значение null. Использование Objects.equal позволяет выполнять проверки равных с помощью NULL-чувствительности, не рискуя исключением NullPointerException. Objects.equal("a", "a"); // returns true Objects.equal(null, "a"); // returns false Objects.equal("a", null); // returns false Objects.equal(null, null); // returns true

Ответ 24

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

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

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

Ответ 25

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

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

Ответ 26

Когда вы хотите сохранить и получить свой пользовательский объект в качестве ключа в Map, вы всегда должны переопределять 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 будут только 1, потому что они равны.

Ответ 27

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(), он может сравнивать контент и давать правильный результат.

Ответ 28

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

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

Ответ 29

В соответствии с java-документацией разработчики должны переопределять оба метода для достижения полностью работоспособного механизма равенства, а его недостаточно, чтобы просто реализовать метод equals().

Если два объекта равны в соответствии с методом equals (Object), тогда вызов метода hashcode() на каждом из двух объектов должен тот же целочисленный результат.

Переопределение equals() будет служить вашим потребностям при проверке равенства двух нормальных объектов и также будет работать с вами при поиске элемента внутри списка.

Однако при работе с хеширующими структурами данных вы будете терпеть неудачу: HashSet, HashMap, HashTable..

Этот учебник подробно описывает вместе с примерами, почему необходимо всегда переопределять equals() и hashcode() вместе. Это стоит прочитать, проверьте его.

Ответ 30

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

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

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

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

Но в любом случае, я думаю, что "правило" написано обратно. В то же время я буду избегать использования "равных" для методов тестирования равенства: - (