Equals java что это
Java Challengers #4: Сравнение объектов с equals() и hashCode()
В преддверии запуска нового потока по курсу «Разработчик Java» мы продолжаем перевод серии статей Java Challengers, предыдущие части которых можно прочитать по ссылкам ниже:
В этой статье вы узнаете, как связаны между собой методы equals() и hashCode() и как они используются при сравнении объектов.
Без использования 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
Выполним следующий код:
Во втором примере, в памяти создастся два объекта.
Эквивалентность и хеш-код тесно связанны между собой, поскольку хеш-код вычисляется на основании содержимого объекта (значения полей) и если у двух объектов одного и того же класса содержимое одинаковое, то и хеш-коды должны быть одинаковые (см. правило 2).
Класс Object
При сравнение объектов, операция “ == ” вернет true лишь в одном случае — когда ссылки указывают на один и тот-же объект. В данном случае не учитывается содержимое полей.
Заглянем в исходный код метода hashCode() в классе Object :
При вычислении хэш-кода для объектов класса Object по умолчанию используется Park-Miller RNG алгоритм. В основу работы данного алгоритма положен генератор случайных чисел. Это означает, что при каждом запуске программы у объекта будет разный хэш-код.
Но, как мы помним, должно выполняться правило: “если у двух объектов одного и того же класса содержимое одинаковое, то и хеш-коды должны быть одинаковые ”. Поэтому, при создании пользовательского класса, принято переопределять методы hashCode() и equals() таким образом, что бы учитывались поля объекта.
Это можно сделать вручную либо воспользовавшись средствами генерации исходного кода в IDE. Например, в Eclipse это Source → Generate 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()
Здесь нет элементов по типу E из пред. рисунка
Насколько я понял, точно так никто в этом и не разобрался. Есть много версий:
Сама функция написана не на Java а вообще на C.
И многие другие. В общем каким-то образом она всё же устроена, но самое главное в том, что стандартная реализация .hashcode() со стандартной реализацией .equals() подчиняются правилу, приведённому в самом начале статьи
Основной причиной для изменения метода .hashcode() является то, что желают изменить .equals(), однако смена стандартной реализации .equals() приводит к нарушению правила из начала статьи
Программирование на C, C# и Java
Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы
ОСТОРОЖНО МОШЕННИКИ! В последнее время в социальных сетях участились случаи предложения помощи в написании программ от лиц, прикрывающихся сайтом vscode.ru. Мы никогда не пишем первыми и не размещаем никакие материалы в посторонних группах ВК. Для связи с нами используйте исключительно эти контакты: vscoderu@yandex.ru, https://vk.com/vscode
Метод equals в Java
Часто при программировании нам приходиться проверять равенство объектов. Но как же работает сравнение объектов в языке 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 при сравнении ссылочных переменных сравниваются не сами объекты, а ссылки на объекты.