如果您的对象是可变的,那么它的哈希码随时间变化是可以接受的。当然,您应该更喜欢不可变对象(Effective Java 2nd Edition,Item 15:Minimize mutability)。
这是来自 Josh Bloch 的哈希码配方,来自Effective Java 2nd Edition,第 9 项:当您覆盖 equals 时,请始终覆盖 hashCode:
Effective Java 2nd Edition hash code recipe
- 在名为
result 的int 变量中存储一些恒定的非零值,例如17。
- 为每个字段计算
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。
- 如果该字段是一个数组,则将其视为每个元素都是一个单独的字段。如果数组字段中的每个元素都很重要,您可以使用 1.5 版中添加的
Arrays.hashCode 方法之一。
- 将哈希码
c组合成result如下:result = 31 * result + c;
按原样遵循配方是正确的,即使只有一个字段。只需根据字段的类型执行适当的操作即可。
请注意,有些库实际上可以为您简化此过程,例如HashCodeBuilder 来自Apache Commons Lang,或者只是Arrays.hashCode/deepHashCode 来自java.util.Arrays。
这些库允许您简单地编写如下内容:
@Override public int hashCode() {
return Arrays.hashCode(new Object[] {
field1, field2, field3, //...
});
}
Apache Commons 语言示例
这是一个更完整的示例,它使用 Apache Commons Lang 的构建器来促进方便且易读的 equals、hashCode、toString 和 compareTo:
import org.apache.commons.lang.builder.*;
public class CustomType implements Comparable<CustomType> {
// constructors, etc
// let's 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 第 2 版
- 第 8 项:覆盖
equals 时遵守总合同
- 第 9 项:当您覆盖
equals 时,请始终覆盖 hashCode
- 第 10 项:始终覆盖
toString
- 第 12 项:考虑实施
Comparable
- 第 2 项:在面对许多构造函数参数时考虑使用构建器