Equals java что это

Java Challengers #4: Сравнение объектов с equals() и hashCode()

В преддверии запуска нового потока по курсу «Разработчик Java» мы продолжаем перевод серии статей Java Challengers, предыдущие части которых можно прочитать по ссылкам ниже:

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

Equals java что это. Смотреть фото Equals java что это. Смотреть картинку Equals java что это. Картинка про Equals java что это. Фото Equals java что это

Без использования equals() и hashCode() для сравнения состояния двух объектов нам нужно писать много сравнений » if «, сравнивая каждое поле объекта. Такой подход делает код запутанным и трудным для чтения. Работая вместе, эти два метода помогают создавать более гибкий и согласованный код.

Исходный код для статьи находится здесь.

Переопределение equals() и hashCode()

Переопределение метода (method overriding) — это приём при котором поведение родительского класса или интерфейса переписывается (переопределяется) в подклассе (см. Java Challengers #3: Полиморфизм и наследование, анг.). В Java у каждого объекта есть методы equals() и hashCode() и для правильной работы они должны быть переопределены.

Это native — метод, который написан на другом языке, таком как Си, и он возвращает некоторый числовой код, связанный с адресом памяти объекта. (Если вы не пишете код JDK, то не важно точно знать, как работает этот метод.)
Примечание переводчика: про значение, связанное с адресом сказано не совсем корректно (спасибо vladimir_dolzhenko). В HotSpot JVM по умолчанию используются псевдослучайные числа. Описание реализации hashCode() для HotSpot, есть здесь и здесь.

Сравнение объектов с equals()

Метод equals() используется для сравнения объектов. Чтобы определить одинаковые объекты или нет, equals() сравнивает значения полей объектов:

Во втором сравнении проверяется, является ли переданный объект null и какой у него тип. Если переданный объект другого типа, то объекты не равны.

Наконец, equals() сравнивает поля объектов. Если два объекта имеют одинаковые значения полей, то объекты совпадают.

Анализ вариантов сравнения объектов

Затем снова сравниваем два объекта Simpson :

Наконец, давайте сравним объект Simpson и экземпляр класса Object :

equals() в сравнении с ==

На первый взгляд кажется, что оператор == и метод equals() делают одно и то же, но, на самом деле, они работают по-разному. Оператор == сравнивает, указывают ли две ссылки на один и тот же объект. Например:

Во следующем примере используем переопределенный метод equals() :

Идентификация объектов с hashCode()

Использование equals() и hashCode() с коллекциями

Классы, реализующие интерфейс Set (множество) должны не допускать добавления повторяющихся элементов. Ниже приведены некоторые классы, реализующие интерфейс Set :

Посмотрим на часть реализации метода add() в HashSet :

Перед добавлением нового элемента HashSet проверяет, существует ли элемент в данной коллекции. Если объект совпадает, то новый элемент вставляться не будет.

Рекомендации по использованию equals() и hashCode()

Таблица 1. Сравнение хэш-кодов

Этот принцип в основном используется в коллекциях Set или Hash по соображениям производительности.

Правила сравнения объектов

Таблица 2.Сравнение объектов с hashCode()

Таблица 3. Сравнение объектов с equals()

Решите задачку на equals() и hashCode()

Для начала, внимательно изучите следующий код :

Сначала проанализируйте код, подумайте, какой будет результат. И только потом запустите код. Цель в том, чтобы улучшить ваши навыки анализа кода и усвоить основные концепции Java, чтобы вы могли сделать свой код лучше.

Какой будет результат?.

Что произошло? Понимание equals() и hashCode()

Первый объект в наборе будет вставлен как обычно:

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

Наконец, следующий объект Simpson имеет то же значение имени, что и первый объект. В этом случае объект вставляться не будет:

Ответ

Правильный ответ — B. Вывод будет:

Частые ошибки с equals() и hashCode()

Что нужно помнить о equals() и hashCode()

Изучите больше о Java

Традиционно жду ваши комментарии и приглашаю на открытый урок, который уже 18 марта проведет наш преподаватель Сергей Петрелевич

Источник

Разбираемся с hashCode() и equals()

Что такое хеш-код?

Если очень просто, то хеш-код — это число. На самом деле просто, не так ли? Если более точно, то это битовая строка фиксированной длины, полученная из массива произвольной длины (википедия).

Пример №1
Выполним следующий код:

Вторая часть объяснения гласит:

полученная из массива произвольной длины.

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

Подведём итог:

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

Понятие эквивалентности. Метод equals()

Начнем с того, что в java, каждый вызов оператора new порождает новый объект в памяти. Для иллюстрации создадим какой-нибудь класс, пускай он будет называться “BlackBox”.

Пример №2
Выполним следующий код:

Во втором примере, в памяти создастся два объекта.

Equals java что это. Смотреть фото Equals java что это. Смотреть картинку Equals java что это. Картинка про Equals java что это. Фото Equals java что это

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

Класс Object

При сравнение объектов, операция “ == ” вернет true лишь в одном случае — когда ссылки указывают на один и тот-же объект. В данном случае не учитывается содержимое полей.

Equals java что это. Смотреть фото Equals java что это. Смотреть картинку Equals java что это. Картинка про Equals java что это. Фото Equals java что это

Заглянем в исходный код метода hashCode() в классе Object :

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

Но, как мы помним, должно выполняться правило: “если у двух объектов одного и того же класса содержимое одинаковое, то и хеш-коды должны быть одинаковые ”. Поэтому, при создании пользовательского класса, принято переопределять методы hashCode() и equals() таким образом, что бы учитывались поля объекта.
Это можно сделать вручную либо воспользовавшись средствами генерации исходного кода в IDE. Например, в Eclipse это SourceGenerate hashCode() and equals().

В итоге, класс BlackBox приобретает вид:

Теперь методы hashCode() и equals() работают корректно и учитывают содержимое полей объекта:

Кому интересно переопределение в ручную, можно почитать Effective Java — Joshua Bloch, chapter 3, item 8,9.

Источник

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

В Java так устроено, что любой класс, который вы определяете, наследуется от класса Object. Таким образом класс Object является суперклассом любого класса в любой программе.

Прежде всего я должен описать главные правила для любых реализаций этих двух методов, которые нужно обязательно соблюдать, запомнить как аксиому:

1). Если x.equals(y) == true, то обязательно hashcode(x) == hashcode(y)

2) Если hashcode(x) == hashcode(y), то не обязательно x.equals(y) == true

Отношение эквивалентности (алгебра)

симметричным (для любых x, y выполняется: если x = y, то y = x)

рефлексивным (для любого x выполняется: x = x)

транзитивным (для любых x, y, z выполняется: если x = y и y = z, то x = z)

Метод .equals() в классе Object реализован примерно следующим образом:

Фактически он делает следующее: Он принимает в качестве аргумента ссылочную переменную и проверяет, ссылается ли они на тот же объект (ту же область памяти, если быть точнее), что и объект, к которому мы применили метод .equals().

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

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

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

Конкретную кодовую реализацию я приводить не буду, потому что она не так важна, как сама идея

Это и другие возможные переопределения метода .equals() мало того, что расширяют круг наших возможностей, так ещё и не лишают старых, ведь мы по прежнему имеем возможность проверять, ссылаются ли две ссылки на одну область памяти, используя операнд ==, вместо прежнего .equals()

Сюръекция (алгебра)

Если немного более подробно разобрать это определение, то мы увидим следующее:

Даже несколько элементов из X могут быть сопоставлены одному и тому же элементу из Y (это называется коллизией).

Возможно есть такое элемент из X, и даже возможно не один, что он не сопоставлен никакому элементу из Y. (см. рисунок, всё интуитивно)

Что происходит в java?

Метод .hashcode() как-раз осуществляет сюръекцию. Множеством X выступает множество всевозможных объектов которые мы можем создать, множеством Y выступает область значений типа данных int. Метод .hashcode() вычисляет каким-то скрытым от нас способом целое число, опираясь на объект, к которому применяется.

Единственное отличие метода .hashcode() от сюръекции в том, что любой объект может быть обработан методом .hashcode()

Equals java что это. Смотреть фото Equals java что это. Смотреть картинку Equals java что это. Картинка про Equals java что это. Фото Equals java что этоЗдесь нет элементов по типу E из пред. рисунка

Насколько я понял, точно так никто в этом и не разобрался. Есть много версий:

Сама функция написана не на Java а вообще на C.

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

Основной причиной для изменения метода .hashcode() является то, что желают изменить .equals(), однако смена стандартной реализации .equals() приводит к нарушению правила из начала статьи

Источник

Программирование на C, C# и Java

Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы

ОСТОРОЖНО МОШЕННИКИ! В последнее время в социальных сетях участились случаи предложения помощи в написании программ от лиц, прикрывающихся сайтом vscode.ru. Мы никогда не пишем первыми и не размещаем никакие материалы в посторонних группах ВК. Для связи с нами используйте исключительно эти контакты: vscoderu@yandex.ru, https://vk.com/vscode

Метод equals в Java

Часто при программировании нам приходиться проверять равенство объектов. Но как же работает сравнение объектов в языке Java? Расскажем вам в этой статье.

Equals java что это. Смотреть фото Equals java что это. Смотреть картинку Equals java что это. Картинка про Equals java что это. Фото Equals java что это

Статья ориентирована на читателей среднего уровня подготовки Java.

Сравнение с помощью == и equals в Java

Приведем пример программного кода:

Но, в чем же разница между этими двумя записями? Какое отличие между оператором сравнения == и методом equals?

Все очень просто. Метод equals в Java при сравнении проверяет и сопоставляет само содержимое объектов (их значения) и на основе этого делает заключение равны они (true) или нет (false).

Оператор == (в случае с примитивными типами данных) сравнивает значения переменных и возвращает результат, НО в случае со ссылочными типами данных (объекты, массивы и т.д.) сравнивает ссылки на объекты в памяти компьютера, и на основании равенства или неравенства ссылок возвращает результат (true или false). Вот в чём отличие метода equals и оператора ==.

Вы можете почитать подробную статью про оба вида типов данных и их различия в соответствующей статье.

Метод equals – это метод класса Object. Каждый объект неявно унаследован от класса Object и они могут вызывать метод equals.

Возвращаясь к примеру, приведенному в начале подраздела, можно сделать вывод о том, каким будет результат операции сравнения в обоих случаях:

Стоит понимать, что вместо класса String, мог быть объявлен любой другой ссылочный тип данных (Object, ArrayList<>, ваш пользовательский класс и т.д.).

Переопределение метода equals в Java

Когда же стоит переопределять метод equals? Это стоит делать только тогда, когда для вашего класса определено понятие логической эквивалентности, которая не совпадает с тождественностью объектов.

Например, для классов Integer и String данное понятие можно применить.

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

Давайте рассмотрим пример кода:

Мы создаем список объектов Address и добавляем объект класса Address с одинаковым конструктором. Добавляем данный объект 2 раза в List, и с помощью цикла удаляем его. Затем выводим в консоль длину списка.

После выполнения программы, в консоли отобразится число 2. Но почему же количество записей в списке не изменилось? Ведь мы пытались их удалить. В примере объекты с равными полями number и street, и всё-таки – почему они не удалились?

При удалении объектов из списка неявно вызывается метод equals и если объект который мы передаем для удаления содержится в списке, то он удаляется. Реализация equals в классе Object проверяет только равенство ссылок. А ссылки у экземпляров классов различные, потому что это совершенно разные объекты, каждый их них был создан с помощью оператора new. Как же это исправить? Необходимо переопределить метод equals в классе Address:

Запустим программу снова и увидим, что количество объектов в списке стало равным нулю.

Скачать исходник программы из этого урока, вы можете, нажав на кнопку ниже:

Правила переопределения метода equals в Java

Какие правила существуют для правильного переопределения метода equals? Их пять.

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

Источник

Методы equals & hashCode: зачем, где используются, как работают

— Теперь я расскажу о не менее полезных методах equals(Object o) & hashCode().

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

— М-да. С чем боролись, на то и напоролись.

— Не вешай нос. Тут все тоже очень хитро.

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

— А можно пример такого метода?

— Конечно. Допустим, у нас есть класс, описывающий математические дроби, тогда он выглядел бы так (для ясности, я переведу английские названия на русский язык):

КодПояснение
i не равно j
Переменные указывают на различные объекты.
Хотя объекты содержат одинаковые данные;
Пример вызова:
Дробь one = new Дробь(2,3);
Дробь two = new Дробь(4,6);
System.out.println(one.equals(two));
Результат вызова будет true.
дробь 2/3 равна дроби 4/6

— Для большей ясности я использовала русские названия. Так можно делать только в обучающих целях.

Теперь разберем пример.

Мы переопределили метод equals, и теперь для объектов класса Дробь у него будет своя реализация.

В этом методе есть несколько проверок:

1) Если переданный для сравнения объект – null, то объекты не равны. Объект, у которого вызвали метод equals ведь точно не null.

2) Проверка на сравнение классов. Если объекты разных классов, то мы не будем пробовать их сравнить, а сразу скажем, что это различные объекты – return false.

3) Со второго класса школы все помнят, что дробь 2/3 равна дроби 4/6. А как это проверить?

2/3 == 4/6
Умножим обе части на оба делителя (6 и 3), получим:
6 * 2 == 4 * 3
12 == 12
Общее правило:
Если
a / b == c / d
То
a * d == c * b

— Поэтому в третьей части метода equals мы преобразуем переданный объект к типу Дробь и сравниваем дроби.

— Понятно. Если бы мы просто сравнивали числитель с числителем и знаменатель со знаменателем, то дробь 2/3 не была бы равной 4/6.

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

— Да, но это только половина дела. Есть еще второй метод – hashCode()

— С методом equals все понятно, а зачем нужен hashCode()?

— Метод hashCode нужен для быстрого сравнения.

У метода equals есть большой минус – он слишком медленно работает. Допустим, у тебя есть множество(Set) из миллиона элементов, и нам нужно проверить, содержит ли оно определенный объект или нет. Как это сделать?

— Можно в цикле пройтись по всем элементам и сравнить нужный объект с каждым объектом множества. Пока не найдем нужный.

— А если его там нет? Мы выполним миллион сравнений, чтобы узнать, что там нет этого объекта? Не многовато ли?

— Да, даже мне понятно, что слишком много сравнений. А что, есть другой способ?

— Да, для этого и используется hashCode().

Метод hashCode() для каждого объекта возвращает определенное число. Какое именно – это тоже решает разработчик класса, как и в случае с методом equals.

Давай рассмотрим ситуацию на примере:

Представь, что у тебя есть миллион 10-тизначных чисел. Тогда в качестве hashCode для каждого числа можно выбрать остаток от его деления на 100.

ЧислоНаш hashCode
123456789090
987655432121
987655422121
988655412121

— Да, с этим понятно. И что нам делать с этим hashCode-числом?

— Вместо того чтобы сравнивать числа, мы будем сравнивать их hashCode. Так быстрее.

И только если hashCode-ы равны, сравнивать уже посредством equals.

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

— Нет, можно обойтись гораздо меньшим числом.

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

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

В данном примере номер общаги – 17 – это и есть hashCode.

Разработчик, который реализует функцию hashCode, должен знать следующие вещи:

А) у двух разных объектов может быть одинаковый hashCode (разные люди могут жить в одной общаге)

В) хеш-коды должны быть выбраны таким образом, чтобы не было большого количества различных объектов с одинаковыми hashCode. Это сведет все их преимущество на нет (Ты пришел в 17 общагу, а там живет пол универа. Облом-с).

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

В нашем примере с Дробью, если бы мы взяли hashCode равный числителю, то дроби 2/3 и 4/6 имели бы разные hashCode. Дроби – одинаковые, equals говорит, что они одинаковые, но hashCode говорит, что они разные. И если перед сравнением с помощью equals сравнивать по hashCode, то получим что объекты разные и до equals просто не дойдём.

— А как правильно реализовать hashCode для дроби?

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

Вариант 1: hashCode равен целой части от деления.

Для дроби 7/5 и 6/5 это будет 1.

Для дроби 4/5 и 3/5 это будет 0.

Но этот вариант плохо годится для сравнения дробей, которые заведомо меньше 1. Целая часть (hashCode) всегда будет 0.

Вариант 2: hashCode равен целой части от деления знаменателя на числитель.

Этот вариант подойдет для случая, когда значение дроби меньше 1. Если дробь меньше 1, значит перевернутая дробь больше 1. А если мы переворачиваем все дроби – это никак не скажется на их сравнении.

Итоговый вариант будет совмещать в себе оба решения:

Проверяем для дробей 2/3 и 4/6. У них должны быть равные hashCode:

Дробь 2/3Дробь 4/6
числитель / знаменатель2 / 3 == 04 / 6 == 0
знаменатель / числитель3 / 2 == 16 / 4 == 1
числитель / знаменатель
+
знаменатель / числитель
0 + 1 == 10 + 1 == 1

— Спасибо, Элли, было действительно интересно.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *