equals() и hashCode() в Java
Вопрос задан
Изменён 4 года 4 месяца назад
Просмотрен 324 раза
Если мы переопределяем equals()
, то обязаны переопределить hasCode()
.
Причем в обоих методах желательно использовать одни и те же поля класса.
Причем, если equals()
дает true
, то и hashCode()
должен быть одинаковым.
Возникает вопрос: а почему бы при переопределении equals()
тогда просто не сравнивать hashCode()
?
Если для двух объектов hashCode
возвращает одинаковое значение — это не означает, что это тот же самый объект.
Но если equals
true
, то их hashCode
будет одинаковый.Алгоритм расчёта хэшкода в классе Object
не учитывает содержимое класса и реализован так, что при каждом запуске программы, даже у экземпляра класса с одним и тем же содержимым хэш будет разным.
Поэтому, если Вы будете сравнивать экземпляры класса по хэшкоду без переопределения метода hashCode()
у Вас всегда будет false
.
Собственно, из-за этого и рекомендуется переопределять метод hashCode()
.
Метод equals()
отвечает за определение эквивалентности объектов. Но, и он в классе Object
не идеален, т.к. сравнивает лишь ссылки на объекты без учёта их содержимого. Потому его то же рекомендуют переопределять.
Как программист будет определять эквивалентность двух объектов с учётом их содержимого в equals()
по большому счёту его личное дело. Только при вычислении хэшкода возможны, т.н. коллизии. Когда два разных объекта имеют одинаковый хэш. По этой причине одинаковые хэшкоды двух и более объектов, ещё не гарантия их эквивалентности. 128 возможных вариантов. Причем полученное, при переборе, входное значение не обязательно будет совпадать с фактическим паролем, но иметь точно такой же хеш.
Метод equals в java объектах как раз и нужен чтоб исключить влияние коллизий на нахождение элементов в коллекциях использующих hashCode и equals, это такие коллекции как HashMap и HashSet. Они используют метод hashCode для определения «места» где должен находится объект, в случае коллизии объекты имеющие идентичный хеш помещаются в список. Поиск среди таких объектов будет осуществляться перебором с вызовом метода equals для каждого элемента списка.
Зарегистрируйтесь или войдите
Регистрация через Google Регистрация через FacebookОтправить без регистрации
ПочтаНеобходима, но никому не показывается
Отправить без регистрации
ПочтаНеобходима, но никому не показывается
By clicking “Отправить ответ”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.
Переопределение в Kotlin. Курс «ООП в Kotlin»
Any
. Это наследование происходит по-умолчанию, поэтому его можно не указывать. В случае явного указания заголовок класса выглядит примерно так:class NumInc(n: Int, gap: Int): Any() { ...
Поскольку дочерние классы наследуют свойства и функции родительского, то все, что есть у Any
, есть у всех остальных классов. Не важно, непосредственно это класс-наследник или наследник наследника.
Класс Any
включает три функции-члена – equals()
toString()
, hashСode()
. Если мы создадим пустой класс, у его объектов все-равно будут методы, унаследованные от Any
:fun main() { val a = Days() val b = Days() println(a. equals(b)) // или a == b println(a.hashCode()) println(b.hashCode()) println(a.toString()) println(a) } class Days {}
Пример выполнения программы:
false 1018547642 1456208737 Days@3cb5cdba Days@3cb5cdba
Метод toString()
в данном случае можно не вызывать, так как функция println()
делает это сама. Этот метод отвечает за преобразование объекта, каких-то данных о нем, к строке. Он должен возвращать строку. В классе
запрограммирован возврат строки, содержащей имя класса и через «собаку» по всей видимости адрес объекта в памяти. Нас может не устраивать такое положение дел, лучше бы, чтобы toString()
выводит что-нибудь более полезное.
Для этого нам потребуется переопределить в нашем классе функцию-член родительского класса. Это значит, что мы создаем в своем классе метод с таким же именем, что и в родительском, а тело метода пишем свое. И когда объект будет вызывать этот метод, будет использована функция-член своего класса, а не родительского.
Если в классе начать писать имя метода родительского класса, то IntelliJ IDEA сама предложит его для переопределения.
После подтверждения код класса будет выглядеть так:
open class NumInc(n: Int, gap: Int) { var number = n var step = gap fun inc() {number += step} fun dec() {number -= step} override fun toString(): String { return super.toString() } }
Слово override
говорит, что данная функция переопределяет родительскую. String
после двоеточия – тип возвращаемых функцией данных, должна возвращаться строка.
super
, которое обозначает родительский класс. Из него мы получаем строку, строка возвращается в функцию toString()
класса NumInc, а уже отсюда с помощью return
она передается в место, откуда вызывался данный метод.В таком варианте, несмотря на то, что мы переопределили функцию в дочернем классе, ничего не изменилось. Как по большому счету выполнялось тело функции родительского класса, так оно и будет выполняться. Просто вызов идет не напрямую, а опосредовано через дочерний класс.
Пусть мы хотим, чтобы метод toString()
возвращал информацию о текущих значениях свойств объектов. Для этого в теле метода удалим вызов аналогичного метода родительского класса и напишем свое тело:
open class NumInc(n: Int, gap: Int) { var number = n var step = gap fun inc() {number += step} fun dec() {number -= step} override fun toString(): String { val n = "number = $number" val s = "step = $step" return "$n \n $s" } }
Если main
будет таким,
fun main() { val a = NumInc(0, 1) val b = NumInc(12, 2) println(a.toString()) println(b) }
результат выполнения таким.
number = 0 step = 1 number = 12 step = 2
Мы можем переопределять не только методы класса Any
, но и своего родительского в его дочернем. Пусть нам нужен класс подобный NumInc, но функции inc()
и dec()
которого уменьшают и увеличивают number на двойной шаг. В этом случае, если этот класс будет дочерним от NumInc, нам придется переопределить функции inc()
и dec()
:
class NumDouble(n: Int, gap: Int): NumInc(n, gap) { override fun inc() { number += step * 2 } override fun dec() { number -= step * 2 } }
Однако мы не сможем этого сделать до тех пор, пока в родительском классе не разрешим переопределять методы с этими именами. Для этого следует объявить как открытые с помощью слова open
:
open class NumInc(n: Int, gap: Int) { var number = n var step = gap open fun inc() {number += step} open fun dec() {number -= step} ... }
Другими словами, в Kotlin, чтобы иметь возможность переопределять методы, недостаточно, чтобы их класс был открытым. Необходимо дополнительно указывать такую возможность для каждого метода.
open
имеют модификатор final
. Мы его не пишем.Однако, если дочерний класс переопределяет метод родительского, то в нем open
не пишется. Вместо этого стоит override
. При этом метод остается открытым. Если у дочернего будет свой дочерний, то он сможет переопределить такой метод. Если требуется исключить возможность дальнейшего переопределения, то перед override
пишут final
.
В примере на скрине мы объявили класс NumDouble открытым и создали от него дочерний NumD2. В нем мы можем переопределить метод inc()
и не можем сделать это с dec()
, потому что в NumDouble объявили метод финальным.
Бывает так, что код переопределяемого метода не сильно отличается от того, что используется в родительском классе. Нам нужно его лишь дополнить. В этом случае из тела метода дочернего класса, вызывается родительский метод через слово super
. После того, как он возвращает поток выполнения в метод дочернего класса, здесь выполняется дополнительный код. Подобное мы уже видели, переопределяя toString()
.
Давайте сделаем это в нашем классе NumDouble, хотя в данном случае большого смысла в этом нет. Тело класса очень простое.
open class NumDouble(n: Int, gap: Int): NumInc(n, gap) { override fun inc() { super.inc() number += step } override fun dec() { super.dec() number -= step } }
Здесь мы изменяем number на один шаг через родительский метод, потом делаем это еще раз уже в дочернем.
Следует отметить, что переопределять можно не только функции-члены класса, но и свойства. Однако нельзя изменить их тип. Поэтому такое переопределение обычно имеет место, когда используются инициирующие значения для полей, и в дочернем классе эти значения должны быть не такими, каковы они в родительском.
open class NumInc { open var number = 0 open var step = 1 open fun inc() {number += step} open fun dec() {number -= step} } open class NumDouble: NumInc() { override var number = 1 override var step = 2 }
Практическая работа:
Пусть свойство step родительского класса NumInc имеет сеттер, проверяющий число на неравенство нулю (нулевой шаг недопустим). В дочернем классе NumDouble такая проверка не нужна. Наследуется ли сеттер, следует ли его переопределять?
PDF-версия курса с ответами к практическим работам
Какая необходимость переопределять методы equals и hashCode в Java
1 ответ на этот вопрос.
0 голосов
Вы должны переопределить hashCode() в каждом классе, который переопределяет equals(). Невыполнение этого требования приведет к нарушению общего контракта для Object.hashCode(), что не позволит вашему классу правильно функционировать в сочетании со всеми коллекциями на основе хэшей, включая HashMap, HashSet и Hashtable.
— from Effective Java , Джошуа Блох
Определив equals() и hashCode(), вы можете повысить удобство использования ваших классов в качестве ключей в коллекциях на основе хэшей. Как поясняется в документе API для hashCode: «Этот метод поддерживается для хеш-таблиц, таких как те, которые предоставляются java.util.Hashtable».
ответил 2 января 2019 г. к Дейзи • 8 120 балловСвязанные вопросы в Java
Оба ответа (Синтаксис) верны. Если … ПОДРОБНЕЕ
ответил 5 июня 2018 г. на Яве к Акрати • 3190 баллов • 553 просмотра- ява
- java-перечисление
- Java-программирование
Список — это упорядоченная последовательность элементов. … ПОДРОБНЕЕ
ответил 26 апр. 2018 г. на Яве к Акрати • 3,190 баллов • 58 036 просмотров- ява
- набор
- список
Чтение json из URL-адреса с помощью url.openStream() и чтение содержимого … ПОДРОБНЕЕ
ответил 13 июня 2018 г. на Яве к самарт395 • 2220 баллов • 4580 просмотров- ява
- Java-JSON
В Java геттеры и сеттеры полностью … ПОДРОБНЕЕ
ответил 26 июня 2018 г. на Яве к Скарлетт • 1,290 баллов • 1609 просмотров- ява
- геттеры
- сеттеры
Насколько я знаю, основной. .. ПОДРОБНЕЕ
ответил 19 июня 2018 г. на Яве к Скарлетт • 1,290 баллов • 9679 просмотров- ява
- полиморфизм
- приоритет
- перегрузка
equals() должен определить эквивалентное отношение и … ПОДРОБНЕЕ
ответил 21 июня 2018 г. на Яве к Сушмита • 6,910 баллов • 337 просмотров- ява
- приоритет
Вы не можете переопределить частный или статический … ПОДРОБНЕЕ
ответил 31 июля 2018 г. на Яве к код.reaper12 • 3 500 баллов • 10 427 просмотров- ява
- функции-в-java
- статический метод
- приоритет
Большую часть времени вам нужно … ПОДРОБНЕЕ
ответил 14 декабря 2018 г. на Яве к Дейзи • 8 120 баллов • 1840 просмотров- ява
- андроид
- приоритет
Вы можете использовать этот метод: String[] strs = . .. ПОДРОБНЕЕ
ответил 25 июля 2018 г. на Яве к самарт395 • 2220 баллов • 2774 просмотра- ява
- массив
- Java-массив
- Java-программирование
- декларация Java-массива
Всякий раз, когда вам нужно изучить конструктор … ПОДРОБНЕЕ
ответил 23 августа 2018 г. на Яве к Дейзи • 8 120 баллов • 3247 просмотров- ява
- java-строки
- функции-в-java
- Когда использовать promise.all()? 12 декабря 2022 г.
- Как найти простые множители целого числа в JavaScript? 12 декабря 2022 г.
- Создание PDF-файла из HTML в div с использованием Javascript 9 декабря 2022 г.
- Перебор массива в JavaScript 9 декабря 2022 г.
- Как получить текущее время только в JavaScript 9 декабря 2022 г.
- 9 0173
Все категории
- ЧатGPT (11)
- Апач Кафка (84)
- Апач Спарк (596)
- Лазурный (145)
- Большие данные Hadoop (1907)
- Блокчейн (1673)
- С# (141)
- С++ (271)
- Консультирование по вопросам карьеры (1060)
- Облачные вычисления (3469)
- Кибербезопасность и этичный взлом (162)
- Аналитика данных (1266)
- База данных (855)
- Наука о данных (76)
- DevOps и Agile (3608)
- Цифровой маркетинг (111)
- События и актуальные темы (28)
- IoT (Интернет вещей) (387)
- Джава (1247)
- Котлин (8)
- Администрирование Linux (389)
- Машинное обучение (337)
- Микростратегия (6)
- PMP (423)
- Power BI (516)
- Питон (3193)
- РПА (650)
- SalesForce (92)
- Селен (1569)
- Тестирование программного обеспечения (56)
- Таблица (608)
- Таленд (73)
- ТипСкрипт (124)
- Веб-разработка (3002)
- Спросите нас о чем угодно! (66)
- Другие (2231)
- Мобильная разработка (395)
- Пользовательский интерфейс UX-дизайн (24)
Подпишитесь на нашу рассылку новостей и получайте персональные рекомендации.
Уже есть учетная запись? .
Почему вы должны переопределять Equals и HashCode в Java?
Все хэш-теги#java#springboot#react#angular#webservices#spring#javascript
LearninJava11 января 2015 г. — Java |
#java#springboot#react#angular#webservices#spring#javascript
Почему вы должны переопределить ?
Возможно, вы видели сотни статей в Интернете о том, почему вы должны переопределять метод хэш-кода, когда вы переопределяете равенство. Но мы уверены, что вы не можете быть убеждены или сбиты с толку относительно того, почему вы должны это делать. В приведенной ниже статье мы попытаемся объяснить то же самое более простыми словами и на нашем очень известном примере с Angry Birds. Причина, по которой вы должны переопределять хеш-код всякий раз, когда вы переопределяете равенство, заключается в том, что вам нужен способ создать тот же объект, который вы хотите найти. Это особенно полезно, когда вы имеете дело с коллекциями. Поиск объекта представляет собой двухэтапный процесс:Во-первых, давайте посмотрим на приведенный ниже пример. Здесь мы создаем 3 объекта AngryBird разных цветов и добавляем их в HashSet. Теперь наша цель — найти КРАСНУЮ птицу. На данный момент мы не будем переопределять метод hashCode(). Давайте запустим пример и посмотрим вывод:
EqualsAndHashCode.java : — Без переопределения hashCode
loading. ..
Вывод: HashCode КРАСНОЙ птицы, которую нужно найти: 1 HashCode птиц в ведрах..
Цвет: ЧЕРНЫЙ HashCode: 2018699554
Цвет: RED HashCode: 3667126 42
Цвет: СИНИЙ HashCode: 1829164700
КРАСНЫЙ сердитая птица присутствует в ведрах? : false
Из вывода, если мы не переопределим метод hashCode(), КРАСНАЯ птица, которую нужно найти, имеет другой хэш-код, чем КРАСНАЯ птица в HashSet. Это означает, что в HashSet нет КРАСНОЙ злой птицы, что неверно. Теперь давайте переопределим hashCode() и посмотрим на результат. Ниже приведен обновленный пример,
EqualsAndHashCode.java : — С переопределенным хэш-кодом равным нулю найти : 0
HashCodes птиц в ведрах..
Цвет : КРАСНЫЙ Хэш-код: 0
Цвет: СИНИЙ Хэш-код: 0
Цвет: ЧЕРНЫЙ Хэш-код: 0
Присутствует ли в ведрах КРАСНАЯ злая птица? : правда
Ааа! После переопределения hashCode() мы теперь видим, что хэш-коды КРАСНЫХ злых птиц совпадают. Также обратите внимание, что теперь мы можем найти объект в HashSet. Мы закончили? Пока нет, обратите внимание, что теперь у нас есть один и тот же хэш-код для всех злых птиц. Это не является нарушением и не причиняет никакого вреда. Если вы не знаете, как переопределить hashCode(), просто верните ноль, как показано.Однако эффективный алгоритм hashCode должен равномерно распределять объекты в корзинах. Чтобы понять это, мы проанализируем с помощью диаграммы, но перед этим давайте переопределим hashCode с помощью лучшего алгоритма. Вот как мы можем это сделать:
EqualsAndHashCode.java : — С измененным и эффективным хэш-кодом
loading…
Вывод: HashCode КРАСНОЙ птицы, которую нужно найти: 1 HashCo des птиц в ведрах..
Цвет: КРАСНЫЙ HashCode: 1
Цвет: СИНИЙ Хэш-код: 2
Цвет: ЧЕРНЫЙ Хэш-код: 3
Присутствует ли в ведрах КРАСНАЯ злая птица? : true
Теперь, поскольку hashCode возвращает размер птицы, а не только ноль, обратите внимание, что каждая птица имеет разный хэш-код в зависимости от ее размера. В чем преимущество с этим?Давайте посмотрим на диаграмму ниже и посмотрим, что произошло с кучей в каждом из вышеперечисленных случаев.
Число в квадратных скобках — это хэш-код. Птица за пределами ведра — это та, которую мы хотим создать в качестве ключа и сопоставить ее с птицей в ведре. Если мы не переопределили метод hashCode(), птицы распределяются по отдельным корзинам, но нет возможности найти конкретную птицу, так как хэш-коды не равны. Когда hashCode возвращал нули, все птицы были втиснуты в одно ведро. Когда hashCode переопределяется эффективным алгоритмом, птицы распределяются равномерно, и мы также можем найти нужную птицу.Надеюсь, после прочтения этой статьи ваши поиски hashCode и equals закончатся… 🙂
Вот и все, ребята !! Удачного кодирования. Если вы считаете, что это помогло вам, продолжайте поддерживать нас по телефонам , или ниже или в статьях в социальных сетях.- Предыдущая статья Пользовательские и демонические потоки
Следующая статья
Состояния потоков — жизненный цикл потока
#java#springboot#react#angular#webservices#spring#javascript
Нравится нам:
Статьи по теме
Java 8 — Stream
В Java 8 появился новый концепция под названием Stream.