来自一个字段的Java hashCode

编辑:准备我的对象以便在HashMap中使用。

在阅读了一些关于如何生成哈希码之后,我现在很困惑。 我的(可能是微不足道的)问题是,当我有一个可以使用的字段时,我应该如何实现hashCode方法? 我可以直接使用fiels吗? 如果我理解正确,hashCode的值在对象的生命周期内不能改变,我只有一个适合这个的ID,但是我已经在其他地方读过,那个人不应该使用ID …这个怎么样,怎么样一个基于这个(唯一且没有变化)值的hashCode函数会是什么样的? equals方法也仅基于id ..

如果您的对象是可变的,则可以随时更改其哈希代码。 当然,您应该更喜欢不可变对象( Effective Java第2版,第15项:最小化可变性 )。

这是来自Effective Java 2nd Edition的 Josh Bloch的哈​​希码配方,第9项:当你重写equals时总是覆盖hashCode

有效的Java第二版哈希码配方

  • 将一些常量非零值(例如17)存储在名为resultint变量中。
  • 为每个字段计算int哈希码c
    • 如果该字段是boolean ,则计算(f ? 1 : 0)
    • 如果字段是byte, char, short, int ,compute (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;

按照原样配方是正确的,即使只有一个字段。 只需根据字段的类型执行相应的操作。

请注意,有些库实际上可以为您简化,例如来自Apache Commons Lang的 HashCodeBuilder ,或者来自java.util.Arrays Arrays.hashCode/deepHashCode

这些库允许您简单地编写如下内容:

 @Override public int hashCode() { return Arrays.hashCode(new Object[] { field1, field2, field3, //... }); } 

Apache Commons Lang的例子

这是一个更完整的示例,使用Apache Commons Lang中的构建器来方便可读的equalshashCodetoStringcompareTo

 import org.apache.commons.lang.builder.*; public class CustomType implements Comparable { // 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
    • EqualsBuilder
    • HashCodeBuilder
    • ToStringBuilder
    • CompareToBuilder
  • 有效的Java第二版
    • 第8项:在超越equals时遵守总合同
    • 第9项:覆盖hashCode时始终覆盖hashCode
    • 第10项:始终覆盖toString
    • 第12项:考虑实施Comparable
    • 第2项:在面对许多构造函数参数时考虑构建器

如果您希望通过该ID标识具有不同ID的对象,则只需返回/比较它即可。

 private final int id; public int hashCode() { return id; } public boolean equals(Object o) { return o instanceof ThisClass && id == ((ThisClass)o).id; } 

使用多少字段来计算hashCode并不重要。 但是使用equals()很重要。 如果A等于B,则它们的hashCode必须相同。

如果你讨厌hashCode :)并且你的对象永远不会被放到基于哈希的容器(HashMap,HashSet ..),只需要单独留下hashCode(),让它的基类来计算hashCode。