来自一个字段的Java hashCode

时间:2010-08-21 07:54:04

标签: java hashcode

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

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

3 个答案:

答案 0 :(得分:15)

如果您的对象是可变的,则可以随时更改其哈希代码。当然,您应该更喜欢不可变对象( Effective Java 2nd Edition,Item 15:Minimize mutability )。

以下是来自 Effective Java 2nd Edition的Josh Bloch的哈​​希码配方,第9项:覆盖hashCode 时始终覆盖equals

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

  • 将一些常量非零值(例如17)存储在名为int的{​​{1}}变量中。
  • 为每个字段计算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,则返回0.
    • 如果字段是数组,则将其视为每个元素都是单独的字段。如果数组字段中的每个元素都很重要,则可以使用版本1.5中添加的null方法之一。
  • 将哈希码Arrays.hashCode合并到c中,如下所示:result

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

请注意,有些库实际上可以为您简化此操作,例如:来自HashCodeBuilderApache Commons Lang,或来自java.util.Arraysresult = 31 * result + c;

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

Arrays.hashCode/deepHashCode

Apache Commons Lang示例

这是一个更完整的示例,使用Apache Commons Lang中的构建器来方便可读@Override public int hashCode() { return Arrays.hashCode(new Object[] { field1, field2, field3, //... }); } equalshashCodetoString

compareTo

这四种方法的编写非常繁琐,而且很难确保所有合同都得到遵守,但幸运的是,图书馆至少可以帮助您轻松完成工作。某些IDE(例如Eclipse)也可以自动为您生成其中一些方法。

另见

  • Apache Commons Lang Builders
  • Effective Java 2nd Edition
    • 第8项:在覆盖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(); } }
    • 时遵守一般合同
    • 第9项:覆盖equals
    • 时始终覆盖hashCode
    • 第10项:始终覆盖equals
    • 第12项:考虑实施toString
    • 第2项:面对许多构造函数参数时考虑构建器

答案 1 :(得分: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;
}

答案 2 :(得分:1)

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

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