JSL规定,调用两个Equals的对象,其hashCode必须相等。
假如我们没有覆盖hashCode,则在和集合类对象HashMap、HashSet、和HashTable一起使用时,会出现问题。例子所示:
public class Money {
private BigDecimal ammount;
private String unit;
public Money(BigDecimal ammount, String unit){
this.ammount= ammount;
this.unit = unit;
}
public BigDecimal getAmmount() {
return ammount;
}
public void setAmmount(BigDecimal ammount) {
this.ammount = ammount;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Money)) {
return false;
}
Money m = (Money) o;
return (ammount == null) || ammount.equals(m.getAmmount()) && (unit == null) || unit.equals(m.getUnit());
}
}
在执行如下测试方法时会发现,print的结果为null
public static void main(String[] args) {
Map m = new HashMap();
m.put(new Money(new BigDecimal(10),"CNY"), 1);
System.out.println(m.get(new Money(new BigDecimal(10),"CNY")));
}
这是因为在Map的get方法中,调用了key的hashCode,如下:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
不覆盖的情况下会直接用object的hashCode,不会产生正确结果。即hash算法把对象放在一个散列桶里,而get方法却从另一个散列桶取值。
如果在Money类里加上如下散列方法:
@Override
public int hashCode() {
int h=17;
h= 31*h+ ammount.hashCode();
h= 31*h+ unit.hashCode();
return h;
}
运行test,则可以得到正确结果:1
至于为什么使用这种hash散列,这里借鉴了经典hash算法的规则,有一点需要强调,能够运用JVM直接完成的算法(移位等)则要使用这些能够提高速度的方法。*31正是运用了新JVM的特性 他可以转换为 (i<<5)-i