Equals and hashcode overriding in java

Override equals and hashCode methods in Java

This post will discuss how to override equals() and hashCode() methods in Java.

The general contract for overriding equals is proposed in item 8 of Josh Bloch’s Effective Java. Ideally equals() method should satisfy the following conditions. It should be:

  • Reflexive : A non-null object should be equal to itself, i.e., x.equals(x) == true .
  • Symmetric : If the first object is equal to a second object, then the second object should be equal to the first object, i.e., x.equals(y) == true if and only if y.equals(x) == true .
  • Transitive : If the first object is equal to a second object and the second object is equal to a third object, then the first object should be equal to the third object, i.e., x.equals(y) == true and y.equals(z) == true , then x.equals(z) == true .
  • Consistent : Multiple calls to equals ( x.equals(y) or y.equals(x) ) must return the same value at any point of time unless either x or y is modified.

Item 9 in Josh Bloch’s Effective Java always asks us to override the hashCode() method if the class overrides equals() . Otherwise, the class object will not behave properly on hash-based collections like HashMap , HashSet , and Hashtable (see why?). Also, keep in mind that

  1. If equals() returns true for two objects, then hashCode() method should also return the same value, i.e., if a.equals(b) == true and b.equals(a) == true , then a.hashCode() == b.hashCode() .
  2. If equals() returns false for two objects, then hashCode() method should return different values, i.e., if a.equals(b) == false or b.equals(a) == false , then a.hashCode() != b.hashCode() .
Читайте также:  Validating xml with xsd in java

Now let’s discuss various ways to override the equals() and hashCode() methods in Java.

1. Java 6 and less

Here’s the recommended process to compute hashCode manually before Java 7:

1. Initialize hashcode by a nonzero value; ideally, a prime number, say 17.

2. For all relevant fields from the object (which are used in the equals method for equality), compute the hashcode c by the following rules and append c into the result using prime multiplier 37 as result = 31 * result + c .

  • For primitive fields, compute (int)f for byte, char or short, (f ? 1 : 0) for a boolean,
    (int)(f ^ (f >>> 32)) for a long ( >>> is unsigned right shift operator) and Float.floatToIntBits(f) and Double.doubleToLongBits(f) , for float and double, respectively.
  • Recursively invoke hashCode on the field that is an object reference.
  • If the field is an array, invoke the Arrays.hashCode() method or compute hashCode for each array element by applying the above rules.
  • If the value of the field is null , return 0.

We should always choose a prime number in the hashCode() method that results in a good hash method that produces unequal hash codes for unequal objects and uniformly distributes all possible hash values across the hash table to avoid a collision.

Источник

Перегрузка методов equals() и hashCode() в Java

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

Переопределение методов equals() и hashCode() в Java

Перегрузка методов equals() и hashCode() в Java - 1

Equals и hashCode являются фундаментальными методами объявленные в классе Object и содержатся в стандартных библиотеках Java. Метод еquals() используется для сравнения объектов, а hashCode — для генерации целочисленного кода объекта. Эти методы широко используются в стандартных библиотеках Java при вставке и извлечении объектов в HashMap . Метод equal также используется для обеспечения хранения только уникальных объектов в HashSet и других Set реализациях, а также в любых других случаях, когда нужно сравнивать объекты. Реализация по умолчанию метода equals() в классе java.lang.Object сравнивает ссылки на адреса в памяти, которые хранят переменные, и возвращает true только в том случае, если адреса совпадают, другими словами переменные ссылаются на один и тот же объект. Java рекомендует переопределять методы equals() и hashCode() , если предполагается, что сравнение должно осуществляться в соответсвии с естественной логикой или бизнес-логикой. Многие классы в стандартных библиотеках Java переопределяют их, например в классе String переопределяется equals таким образом, что возвращается true , если содержимое двух сравниваемых объектов одинаковое. В классе-обертке Integer метод equal переопределяется для выполнения численного сравнения, и так далее. Так как HashMap и HashTable в Java полагаются на методы equals() и hashCode() для сравнения своих key и values , то Java предлагает следующие правила для переопределения этих методов:

  1. Рефлексивность: Объект должен равняться себе самому.
  2. Симметричность: если a.equals(b) возвращает true , то b.equals(a) должен тоже вернуть true .
  3. Транзитивность: если a.equals(b) возвращает true и b.equals(c) тоже возвращает true , то c.equals(a) тоже должен возвращать true .
  4. Согласованность: повторный вызов метода equals() должен возвращать одно и тоже значение до тех пор, пока какое-либо значение свойств объекта не будет изменено. То есть, если два объекта равны в Java, то они будут равны пока их свойства остаются неизменными.
  5. Сравнение null : объект должны быть проверен на null . Если объект равен null , то метод должен вернуть false , а не NullPointerException . Например, a.equals(null) должен вернуть false .

Соглашение между equals и hashCode в Java

  1. Если объекты равны по результатам выполнения метода equals , тогда их hashcode должны быть одинаковыми.
  2. Если объекты не равны по результатам выполнения метода equals , тогда их hashcode могут быть как одинаковыми, так и разными. Однако для повышения производительности, лучше, чтобы разные объекты возвращали разные коды.

Как переопределять метод equals в Java

 @Override public boolean equals(Object obj) < /*1. Проверьте*/if (obj == this) < /*и верните */ return true; > 
 if (obj == null || obj.getClass() != this.getClass())
 Person guest = (Person) obj; return guest.id && (firstName == guest.firstName || (firstName != null && firstName.equals(guest.getFirstName()))) && (lastName == guest.lastName || (lastName != null && lastName .equals(guest.getLastName()))); > 
 /** * Person class with equals and hashcode implementation in Java * @author Javin Paul */ public class Person < private int id; private String firstName; private String lastName; public int getId() < return id; >public void setId(int 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; >@Override public boolean equals(Object obj) < if (obj == this) < return true; >if (obj == null || obj.getClass() != this.getClass()) < return false; >Person guest = (Person) obj; return guest.id && (firstName == guest.firstName || (firstName != null &&firstName.equals(guest.getFirstName()))) && (lastName == guest.lastName || (lastName != null && lastName .equals(guest.getLastName()) )); > @Override public int hashCode() < final int prime = 31; int result = 1; result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); result = prime * result + id; result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); return result; >> 

Распространенные ошибки при переопределении equals в Java

  1. Вместо того, чтобы переопределять метод equals (Override) программист перегружает его (Overload) Синтаксис метода equals() в классе Object определен как public boolean equals(Object obj) , но многие программисты ненароком перегружают метод: public boolean equals(Person obj) — вместо Object в качестве аргумента используют имя своего класса (напр. Person). Эту ошибку сложно обнаружить из-за static binding . Таким образом, если вы вызовете этот метод для объекта своего класса, то метод не просто скомпилируется, а даже сделает это корректно. Однако, если вы положите ваш объект в коллекцию, например ArrayList и вызовете метод contains() , работа которого основана на методе equals() , то метод contains не сможет обнаружить ваш объект.
  2. При переопределении метода equals() не проверять на null переменные, что в конечном итоге заканчивается NullPointerException при вызове equals() . Ниже представлен корректный код.
 firstname == guest.firstname || (firstname != null && firstname.equals(guest.firstname)); 

Подсказки как писать в Java метод equals

  1. Большинство IDE такие как NetBeans, Eclipse и IntelliJ IDEA обеспечивают поддержку генерации методов equals() и hashCode() . В Eclipse нажмите правую кнопку -> source -> generate equals() и hashCode() .
  2. Если в классе есть уникальный бизнес-ключ, то будет достаточно сделать проверку только на равенство этих полей. Как в нашем примере “id” — уникальный номер для каждого Person.
  3. При переопределении hashCode() в Java удостоверьтесь в использовании всех полей, что были использованы в методе equals() .
  4. String и классы-оболочки такие как Integer , Float и Double переопределяют метод equals() , но StringBuffer не переопределяет.
  5. При любой возможности делайте поля immutable используя final переменные в Java.
  6. При сравнении String объектов используйте equals() вместо оператора == .
  7. Два объекта которые логически равны, но загружены из разных ClassLoader не могут быть равными. Помните, что проверка с помощью getClass() вернет false если класс-загрузчик разный.
  8. Используйте @Override аннотацию также для метода hashCode , так как это предупреждает неуловимые ошибки, например возвращаемое значение метода int , однако некоторые программисты возвращают long .

Источник

Оцените статью