来自一个字段的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)存储在名为
result
的int
变量中。 - 为每个字段计算
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中的构建器来方便可读的equals
, hashCode
, toString
和compareTo
:
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项:在面对许多构造函数参数时考虑构建器
- 第8项:在超越
如果您希望通过该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。