• Item 9 覆盖equals时总要覆盖hashCode


    为什么覆盖equals时,总要覆盖hashCode?

     
    原因是,根据Object规范:
    如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
    如果违反这个规定,那么,在使用应用了散列码的集合(HashMap,HashSet,Hashtable)时,就会出现问题。
     
    也就是,两个相等的对象,必须要有相等的散列码(hashCode)。两个对象,进行比较的时候,使用的关键域是一样的,然后,使用这些关键域作为参数来生成散列码。两个对象相应的关键域的值相等,那么,使用相等的关键域,使用同样的生成方法,那么,生成出来的散列码是相等的
     
     
    两个对象相等,那么,它们的hashCode是相等的么?
    答:是的。根据Object规范,这是必须满足的。
     
    两个对象的hashCode相等,那么这两个对象是相等的么?
    答:不一定。因为,equals的实现,有可能是使用其它方式,未必只是简单地比较hashCode方法中用到的关键域。所以,不能根据对象的hashCode来判定两个对象是否相等。
     
    两个对象根据equals(Object)方法进行比较,它们不相等,那么,这两个对象的hasoCde一定是不同的么?
    答:不一定。同样的道理,因为equals方法的实现,未必只是简单地比较hashCode方法中用到的关键域的值。
     
    hashCode的生成方式:
     
    使用---HashCodeBuilder,该类是参考<<effective java>>中描述的规则实现的。
     
    例子:
    import java.util.HashMap;
    import java.util.Map;
    
    public final class PhoneNumber {
      private final short areaCode ;
      private final short prefix ;
      private final short lineNumber ;
    
      public PhoneNumber( int areaCode, int prefix, int lineNumber) {
        rangeCheck(areaCode, 999, "area code");
        rangeCheck(prefix, 999, "prefix" );
        rangeCheck(lineNumber, 9999, "line number");
        this. areaCode = ( short) areaCode;
        this. prefix = ( short) prefix;
        this. lineNumber = (short ) lineNumber;
      }
    
      private static void rangeCheck(int arg, int max, String name) {
        if (arg < 0 || arg > max)
          throw new IllegalArgumentException(name + ": " + arg);
      }
    
      @Override
      public boolean equals(Object o) {
        if (o == this)
          return true;
        if (!(o instanceof PhoneNumber))
          return false;
        PhoneNumber pn = (PhoneNumber) o;
        return pn. lineNumber == lineNumber && pn.prefix == prefix && pn. areaCode == areaCode;
      }
    
      // Broken - no hashCode method!
    
      // A decent hashCode method - Page 48
      // @Override public int hashCode() {
      // int result = 17;
      // result = 31 * result + areaCode;
      // result = 31 * result + prefix;
      // result = 31 * result + lineNumber;
      // return result;
      // }
    
      // Lazily initialized, cached hashCode - Page 49
      // private volatile int hashCode; // (See Item 71)
      //
      // @Override public int hashCode() {
      // int result = hashCode;
      // if (result == 0) {
      // result = 17;
      // result = 31 * result + areaCode;
      // result = 31 * result + prefix;
      // result = 31 * result + lineNumber;
      // hashCode = result;
      // }
      // return result;
      // }
    
      public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
        m.put (new PhoneNumber(707, 867, 5309), "Jenny");
        System.out.println(m.get( new PhoneNumber(707, 867, 5309)));
      }
    }
    
    ---------------------------------------------
    输出结果是:
    null
     
    原因:在覆盖equals方法的时候,没有覆盖hashCode方法。PhoneNumber作为HashMap的K,在main方法中,生成了两个PhoneNumber实例,这两个PhoneNumber实例通过equals方法比较是相等的。但是,PhoneNumber,并没有覆盖hashCode方法,所以,这两个实例具备不同的hashCode。现在要做的就是,覆盖hashCode方法,然后,使通过equals比较相等的两个PhoneNumber实例返回相等的hashCode。
     
    HashCode生成方式,使用HashCodeBuilder:
    下面的代码添加了一个覆盖的hashCode方法,输出结果,就找到了Jenny。
    public final class PhoneNumber {
      private final short areaCode ;
      private final short prefix ;
      private final short lineNumber ;
    
      public PhoneNumber( int areaCode, int prefix, int lineNumber) {
        rangeCheck(areaCode, 999, "area code");
        rangeCheck(prefix, 999, "prefix" );
        rangeCheck(lineNumber, 9999, "line number");
        this. areaCode = ( short) areaCode;
        this. prefix = ( short) prefix;
        this. lineNumber = (short ) lineNumber;
      }
    
      private static void rangeCheck(int arg, int max, String name) {
        if (arg < 0 || arg > max)
          throw new IllegalArgumentException(name + ": " + arg);
      }
    
      @Override
      public boolean equals(Object o) {
        if (o == this)
          return true;
        if (!(o instanceof PhoneNumber))
          return false;
        PhoneNumber pn = (PhoneNumber) o;
        return pn. lineNumber == lineNumber && pn.prefix == prefix && pn. areaCode == areaCode;
      }
    
      @Override
      public int hashCode() {
        return new HashCodeBuilder(17, 37).append(lineNumber).append(prefix ).append(areaCode)
            .toHashCode();
      }
    
      // Broken - no hashCode method!
    
      // A decent hashCode method - Page 48
      // @Override public int hashCode() {
      // int result = 17;
      // result = 31 * result + areaCode;
      // result = 31 * result + prefix;
      // result = 31 * result + lineNumber;
      // return result;
      // }
    
      // Lazily initialized, cached hashCode - Page 49
      // private volatile int hashCode; // (See Item 71)
      //
      // @Override public int hashCode() {
      // int result = hashCode;
      // if (result == 0) {
      // result = 17;
      // result = 31 * result + areaCode;
      // result = 31 * result + prefix;
      // result = 31 * result + lineNumber;
      // hashCode = result;
      // }
      // return result;
      // }
    
      public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
        m.put(new PhoneNumber(707, 867, 5309), "Jenny");
        System.out.println(m.get( new PhoneNumber(707, 867, 5309)));
      }
    }
    

    确定关键域,关键域指的是覆盖的equals方法中涉及到的每个域;然后,使用这些关键域,作为参数,用它们生成一个hashCode。这个可以通过HashCodeBuilder来实现,具体参照上述代码。

      

     
     
  • 相关阅读:
    CentOS中JAVA_HOME的环境变量设置
    Macserver服务更新经常使用的几个shell命令
    一个技术派创业者的反思
    巴斯卡三角形
    iOS中基于 Socket 的 C/S 结构网络通信(中)
    poj 3267 The Cow Lexicon (动态规划)
    Android入门:短信和拨打电话
    HDUOJ--4888--Redraw Beautiful Drawings【isap】网络流+判环
    Dynamics CRM 2015 New Feature (9): Services Changes
    Class 找出一个整形数组中的元素的最大值
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4364304.html
Copyright © 2020-2023  润新知