What is default hashcode in java

Методы .equals и .hashcode в Java. Отличия реализации по умолчанию от реализации на практике

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

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

Это означает, что абсолютно любой класс содержит методы, которые определены в классе Object. Методы .equals() и .hashcode() — одни из них.

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

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

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

Метод .equals()

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

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

Отношение эквивалентности — это бинарное (бинарное — значит между двумя) отношение, которое является:

Таким образом, если на множестве определено отношение эквивалентности, множество можно разделить на подмножества — классы эквивалентности.

Каждый класс эквивалентности содержит внутри себя только те элементы, которые эквиваленты (более формально — находятся в отношении эквивалентности) между собой.

Реализация .equals() по умолчанию

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

public boolean equals(Object x)

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

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

Такая реализация не противоречит математической идеологии, описанной выше. Однако на практике метод .equals() часто переопределяют в подклассах.

Как и зачем переопределяют метод .equals()?

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

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

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

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

Метод .hashcode()

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

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

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

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

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

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

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

Здесь нет элементов по типу E из пред. рисунка

Реализация .hashcode() по умолчанию?

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

  • Значение .hashcode() — это область памяти, где лежит объект
  • Значение .hashcode() — это число, создаваемое генератором случайных чисел в какой-то момент
  • Сама функция написана не на Java а вообще на C.

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

Как и зачем переопределяют метод .hashcode()?

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

Второстепенной причиной для изменения метода .hashcode() является то, что желают изменить вероятность коллизии (эта причина встречается реже)

Источник

Работа с методами hashCode() и equals() в языке Java

Java-университет

Работа с методами hashCode() и equals() в языке Java - 1

В этом посте я изложу свое понимание методов hashCode() и equals() . Я хочу рассказать об их реализации по умолчанию, а также о том как корректно переопределить их. Я также напишу о реализации этих методов, используя Apache Common package’s вспомогательные классы. Содержание этого поста:

  1. Использование hashCode() и equals() .
  2. Переопределение поведения по умолчанию.
  3. Переопределение hashCode() и equals() , используя Apache Commons Lang.
  4. То, что важно помнить.
  5. Особое Внимание При Использовании ORM.

Методы hashCode() и equals() были определены в классе Object , который является родительским классом для объектов java. Поэтому все java объекты наследуют от этих методов реализацию по умолчанию.

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

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

Переопределение поведения по умолчанию

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

 public class Employee < private Integer id; private String firstname; private String lastName; private String department; public Integer getId() < return id; >public void setId(Integer id) < this.id = id; >public String getFirstname() < return firstname; >public void setFirstname(String firstname) < this.firstname = firstname; >public String getLastName() < return lastName; >public void setLastName(String lastName) < this.lastName = lastName; >public String getDepartment() < return department; >public void setDepartment(String department) < this.department = department; >> 

Описанный выше класс Employee имеет некоторые основные атрибуты и методы доступа. Сейчас рассмотрим простую ситуацию, где необходимо сравнить два объекта класса Employee .

Не нужно быть ясновидящим, чтобы догадаться, что написанный выше метод вернет “false”. Но правильно ли это на самом деле, учитывая, что эти оба объекта одинаковые? В real time application метод должен вернуть true. Чтобы достигнуть корректного поведения, нам нужно переопределить метод equals() , как и сделано ниже:

 public boolean equals(Object o) < if(o == null) < return false; >if (o == this) < return true; >if (getClass() != o.getClass()) < return false; >Employee e = (Employee) o; return (this.getId() == e.getId()); > 

Добавьте этот метод в свой класс Employee , и проверка на эквивалентность вернет “true”. Однако, всё ли мы сделали? Пока нет. Давайте проверим наш модифицированный класс еще одним способом.

 import java.util.HashSet; import java.util.Set; public class EqualsTest < public static void main(String[] args) < Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Печатает 'true' System.out.println(e1.equals(e2)); Set employees = new HashSet(); employees.add(e1); employees.add(e2); //Печатает два объекта System.out.println(employees); >> 

Команда System.out.println(employee) распечатывает два объекта. Если оба объекта были эквивалентны, а в Set содержатся только уникальные объекты, то внутри HashSet должен быть только один экземпляр, т.е. оба объекта ссылаются на одинаковые экземпляры класса Employee . Что же мы упустили? Мы упустили второй важный метод hashCode() . Как сказано в документации java, если вы переопределяете метод equals() , то вы обязаны переопределить метод hashCode() . Итак, давайте добавим ещё один метод в наш класс Employee .

 @Override public int hashCode()

Мы добавили один раз этот метод в наш класс, и на печать будет выведен только один объект, и, таким образом, проверка эквивалентности е1 и е2 показала true.

Переопределение hashCode() и equals() , используя Apache Commons Lang

Apache Commons предоставляет два замечательных вспомогательных класса для вызова методов hashCode() и equals() . Ниже смотрим использование:

 import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; public class Employee < private Integer id; private String firstname; private String lastName; private String department; public Integer getId() < return id; >public void setId(Integer id) < this.id = id; >public String getFirstname() < return firstname; >public void setFirstname(String firstname) < this.firstname = firstname; >public String getLastName() < return lastName; >public void setLastName(String lastName) < this.lastName = lastName; >public String getDepartment() < return department; >public void setDepartment(String department) < this.department = department; >@Override public int hashCode() < final int PRIME = 31; return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME). toHashCode(); >@Override public boolean equals(Object o) < if (o == null) return false; if (o == this) return true; if (o.getClass() != getClass()) return false; Employee e = (Employee) o; return new EqualsBuilder(). append(getId(), e.getId()). isEquals(); >> 

Работа с методами hashCode() и equals() в языке Java - 2

С другой стороны, если вы используете один из редакторов кода, они также должны быть способны вызывать некоторые хорошие структуры для вас. Например, если в Eclipse IDE кликнуть правой клавишей на class >> sourse > Generating hashCode() and equals() … будет сгенерирована очень хорошая реализация для вас. То, что важно помнить.

  1. Всегда используйте те же атрибуты объекта для вызова и hashCode() и equals() . Как раз в нашем случае, мы использовали employee id .
  2. Метод equals() должен быть устойчивым (если объект не изменялся, метод должен возвращать то же самое значение).
  3. Всякий раз, когда a.equals(b) , то a.hashCode() должно быть таким же, как b.hashCode() .
  4. Если вы переопределили один метод, то обязательно должны переопределить второй.

Особое Внимание При Использовании ORM

Если вы имеете дело с ORM (ru.wikipedia.org/wiki/ORM), то всегда используйте геттеры и никогда не используйте в hashCode() и equals() ссылки на поля. Это потому, что в ORM, время от времени поля загружаются при помощи отложенной загрузки (lazy load) и не доступны, пока не вызваны их геттеры. Например, в нашем классе Employee , мы используем e1.id == e2.id . Вполне возможно, что поля i d загружены с помощью отложенной загрузки. Одно из полей может быть равно 0 или null и мы получим некорректное поведение. Но, если используется e1.getId() == e2.getId() , мы можем быть уверены, даже если поля были загружены с помощью отложенной загрузки; вызов геттера первым заполнит поле. Это всё, что я знаю о методах hashCode() и equals() . Надеюсь, что это где-нибудь кому-нибудь поможет. Удачи в учебе!! p.s. Это моя первая попытка перевода. Постаралась передать всё как можно ближе к тому, что хотел сказать автор. Если есть замечания, пожалуйста, напишите в комментариях. Строго не судите :-))) Oригинал статьи

Источник

Читайте также:  Convert int to bits python
Оцените статью