改写equals时总是要改写hashCode

来源:互联网 发布:如何下载spss软件 编辑:程序博客网 时间:2024/04/30 00:37
java.lnag.Object中对hashCode的约定:
  1. 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
  2. 如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
  3. 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。

看个不改写hashCode导致使用hashMap不能出现预期结果的例子:
public final class PhoneNumber{
private final short areaCode;
private final short exchange;
private final short extension;

public PhoneNumber(int areaCode,int exchage,int extension){
rangeCheck(areaCode,999,"area code");
rangeCheck(exchange,999,"exchange");
rangeCheck(extension,9999,"extension");
this.areaCode=(short) areaCode;
this.exchange=(short) exchange;
this.extension=(short)extension;
}
private static void rangeCheck(int arg,int max, String name){
if(arg<0 || arg>max) throw new IllegalArgumentException(name+":"+arg);
}
public boolean equals(Object o){
if (o == this) reutrn true;
if (!(o instanceof PhoneNumber)) return false;
PhoneNumber pn=(PhoneNumber)o;
return pn.extension==extension && pn.exchange=exchange && pn.areaCode=areaCode;
}
//No hashCode method
...
}
现在有以下几行程序:
Map m=new HashMap();
m.put(new PhoneNumber(1,2,3),"Jenny");
则m.get(new PhoneNumber(1,2,3))的返回值什么?
  虽然这个实例据equals是相等的,但由于没改写hashCode而致两个实例的散列码并不同(即违反第二条要求),因则返回的结果是null而不是"Jenny".
  理想情况下,一个散列函数应该把一个集合中不相等的实例均匀地分布到所有可能的散列值上,下面是接近理想的“处方”:

  1. 把某个非零常数值(如17)保存在一个叫result的int类型的变量中;
  2. 对于对象中每个关键字域f(指equals方法中考虑的每一个域),完成以下步骤:
    1. 为该域计算int类型的散列码c:
      1. 如果该域是bloolean类型,则计算(f?0:1)
      2. 如果该域是byte,char,short或int类型,则计算(int)f
      3. 如果该域是long类型,则计算(int)(f^(>>>32))
      4. 如果该域是float类型,则计算Float.floatToIntBits(f)
      5. 如果该域是double类型,则计算Double.doubleToLongBits(f)得一long类型值,然后按前述计算此long类型的散列值
      6. 如果该域是一个对象引用,则利用此对象的hashCode,如果域的值为null,则返回0
      7. 如果该域是一个数组,则对每一个数组元素当作单独的域来处理,然后安下一步的方案来进行合成


    2. 利用下面的公式将散列码c 组合到result中。result=37*result+c;

  3. 检查“相等的实例是否具有相等的散列码?”,如果为否,则修正错误。

  依照这个处方,得PhoneNumber的hashCode方法:
public int hashCode(){
int result=17;
result=37*result+areaCode;
result=37*result+exchange;
result=37*result+extension;
return result;
}
  如果计算散列码的代价比较高,可以考虑用内部保存这个码,在创建是生成或迟缓初始化生成它。不要试图从散列码计算中排除掉一个对象的关键部分以提高性能。
原创粉丝点击