Ответ 1
Если ваш объект изменен, тогда приемлемо, чтобы его хэш-код менялся со временем. Конечно, вы должны предпочесть непреложные объекты (Effective Java 2nd Edition, Item 15: Minimize mutability).
Вот рецепт хэш-кода от Джоша Блоха, из Effective Java 2nd Edition, Item 9: Всегда переопределяйте hashCode
при переопределении equals
:
Эффективный рецепт хеш-кода Java 2nd Edition
- Храните некоторое постоянное ненулевое значение, например 17, в переменной
int
, называемойresult
. - Вычислить хэш-код
int
c
для каждого поля:- Если поле является
boolean
, вычислить(f ? 1 : 0)
- Если поле
byte, char, short, int
, вычислите(int) f
- Если поле является
long
, вычислить(int) (f ^ (f >>> 32))
- Если поле является
float
, вычислитьFloat.floatToIntBits(f)
- Если поле является
double
, вычислитеDouble.doubleToLongBits(f)
, тогда хеш получимlong
, как указано выше. - Если поле является ссылкой на объект, и этот класс
equals
метод сравнивает поле путем рекурсивного вызоваequals
, рекурсивно вызываетhashCode
в поле. Если значение поляnull
, верните 0. - Если поле является массивом, рассматривайте его так, как будто каждый элемент является отдельным полем. Если каждый элемент в массиве значителен, вы можете использовать один из методов
Arrays.hashCode
, добавленных в версию 1.5.
- Если поле является
- Объедините хэш-код
c
вresult
следующим образом:result = 31 * result + c;
Было бы правильным следовать рецепту, как есть, даже с одним полем. Просто выполните соответствующее действие в зависимости от типа поля.
Обратите внимание, что есть библиотеки, которые фактически упрощают это для вас, например. HashCodeBuilder
из Apache Commons Lang или просто Arrays.hashCode/deepHashCode
from java.util.Arrays
.
Эти библиотеки позволяют просто написать что-то вроде этого:
@Override public int hashCode() {
return Arrays.hashCode(new Object[] {
field1, field2, field3, //...
});
}
Пример Apache Commons Lang
Вот более полный пример использования строителей Apache Commons Lang для облегчения удобных и читаемых equals
, hashCode
, toString
и compareTo
:
import org.apache.commons.lang.builder.*;
public class CustomType implements Comparable<CustomType> {
// constructors, etc
// let say that the "significant" fields are field1, field2, field3
@Override public String toString() {
return new ToStringBuilder(this)
.append("field1", field1)
.append("field2", field2)
.append("field3", field3)
.toString();
}
@Override public boolean equals(Object o) {
if (o == this) { return true; }
if (!(o instanceof CustomType)) { return false; }
CustomType other = (CustomType) o;
return new EqualsBuilder()
.append(this.field1, other.field1)
.append(this.field2, other.field2)
.append(this.field3, other.field3)
.isEquals();
}
@Override public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(field1)
.append(field2)
.append(field3)
.toHashCode();
}
@Override public int compareTo(CustomType other) {
return new CompareToBuilder()
.append(this.field1, other.field1)
.append(this.field2, other.field2)
.append(this.field3, other.field3)
.toComparison();
}
}
Эти четыре метода могут быть заведомо утомительными для записи, и может быть трудно обеспечить соблюдение всех контрактов, но, к счастью, библиотеки могут, по крайней мере, облегчить работу. Некоторые IDE (например, Eclipse) также могут автоматически генерировать некоторые из этих методов для вас.
См. также
- Apache Commons Lang Builders
- Эффективное Java 2nd Edition
- Пункт 8: соблюдать общий контракт при переопределении
equals
- Пункт 9: Всегда переопределять
hashCode
при переопределенииequals
- Пункт 10: Всегда переопределять
toString
- Пункт 12: Рассмотрите возможность выполнения
Comparable
- Пункт 2. Рассмотрим конструктор, столкнувшись со многими параметрами конструктора.
- Пункт 8: соблюдать общий контракт при переопределении