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

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

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

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

Ответы [ 28 ]

5 голосов
/ 01 января 2014

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

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

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

5 голосов
/ 14 июля 2015
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() для определения ведро и использует equals() метод, чтобы найти, является ли значение уже присутствует в ведре. Если нет, он будет добавлен, в противном случае он будет заменен текущим значением
  • get ('key') будет использовать hashCode(), чтобы сначала найти Entry (bucket) и equals() чтобы найти значение в Entry

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

Карта <<strong> A >

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

если равно не отменено

Карта <<strong> A >

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

Если хэш-код не переопределен

Карта <<strong> 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 Equal Contract

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

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

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

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

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

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

4 голосов
/ 12 ноября 2010

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

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

4 голосов
/ 28 августа 2017

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():

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

    public int hashCode(){  
      return this.color.hashCode(); 
    }
    
3 голосов
/ 15 февраля 2010

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

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

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

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

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

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

3 голосов
/ 28 июля 2013

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

3 голосов
/ 28 августа 2016

Методы Equals и Hashcode в Java

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

Реализация:

public boolean equals (Object obj)

public int hashCode ()

enter image description here

public boolean equals (Object obj)

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

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

Симметрично: для любых ссылочных значений 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, x.equals (null) должен возвращать ложь.

public int hashCode ()

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

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

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

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

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

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

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

Ресурсы:

Javaranch

Изображение

2 голосов
/ 02 сентября 2017

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

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

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

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

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

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

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

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

Вот и все!

...