четверг, 9 марта 2017 г.

hashCode и equals на пальцах

Недавно пытался объяснить в чем смысл взаимосвязи между методами hashCode и equals в Java. В итоге получилось вот такое:


Давайте представим, что есть два ведра с совершенно камнями. Если в них одинаковое количество камней, то можно сказать, что содержимое ведер равно. Это очень важно - два ведра по сути равны, но это РАЗНЫЕ ведра. Вот для такого случая и надо переопределять метод equals - чтобы два РАЗНЫХ ведра стали ОДИНАКОВЫМИ.

Теперь добавим еще ведра с камнями - штук 30 или 100. Там может быть несколько иное количество камней. Близкое, но иное. А в некоторых сильно иное. Все ведра закрыты крышкой на замке - открывать долго и нудно. Сосчитать все камни внутри тоже непросто и занимает много времени. Ваша задача - найти ведро с определенным количеством камней.

Достаточно логично предположить, что одинаковое количество камней имеют одинаковую массу. Ваш алгоритм поиска в этом случае может быть ускорен. Если ведро очень легкое, то его можно даже не открывать. Если очень тяжелое - аналогично.
Теперь допустим, что камень весит настолько мало, что разница в несколько камней не ощутима при взвешивании. Уровень дискретности (уровень ошибки) измерителя веса ведра слишком большой. В этом случае можно сказать, что только для ведер с подходящим весом вы будете открывать крышку и считать камни. Потому что мы только что говорили - одинаковый вес не гарантия одинакового количества камней. Вы не можете ощутить вес отдельного камня, поэтому определить по весу ведра точное количество камней вы не можете. Но отобрать НЕ ПРИГОДНЫЕ ведра вы можете достаточно быстро.

Так вот hashCode в данном случае - это вес. А количество камней - equals.

Если мы оставим hashCode от класса Object, то у КАЖДОГО объекта он будет уникальный. Еще раз - УНИКАЛЬНЫЙ код для КАЖДОГО ведра. Вне зависимости от того, какое количество камней у него внутри. Даже если количество камней в них одинаковое. Бредово немного, но попробуйте это увидеть. В этом случае вы быстрый поиск не сможете сделать. Придется открывать каждое ведро и считать камни.

В случае, если мы можем использовать новую версию hashCode, которая считает вес в зависимости от количества камней (пусть даже мы немного сшибаемая из-за большого шага нашего измерителя веса), то для одинакового количества камней мы ОБЯЗАНЫ иметь ОДИНАКОВЫЙ вес. Но мы понимаем, что вес может быть одинаковый и у разного количества камней.

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

И вот то, о чем постоянно пишут в документации:
- Если объекты одинаковые (equals возвращает true), то и hashCode у них должен возвращать одинаковое значение
- Если даже объекты возвращают одинаковое значение для ashCode - это не значит, что они равные - их надо еще проверить на equals.

Может такое объяснение кому-то поможет. Удачи.

Комментариев нет:

Отправить комментарий