java中需要重写的hashcode() toString() 和equals()方法

问题一:我们自定义类中的toString()、hashCode()和 equals(Object obj)均继承自Object,其中equals()方法是比较两对象的地址是否相同,hashCode()方法返回的是该对象本身的内存地址。但这个需 求不能满足我们的需求。如问题二。(JDK中其它类都已重写了上述方法,不作考虑)

    问题二:在我们往HashSet中添加自定义对象的时候(HashSet中不能添加相同的对象),HashSet会先将已存在的对象与欲添加的对象进行一 一对比,相同的对象不允许再添加。其比较规则为:如果两对象的hashCode()方法返回值不一样,肯定不是相同的对象,可添加;如果 hashCode()一样,则再判断equals()返回是否为真,不为真则肯定不是相同的对象,可添加,为真由证明两对象完全相同,不再添加到 HashSet中。那按Object()中的hashCode()方法,则只要内存不一样,则两对象的hashCode就不一样,则认为两对象不相同,可 往HashSet中添加,这违背了我们实际需求。

    结论:我们自定义类如果想往HashSet等集合中添加时,必须重写equals(Object obj)和hashCode()方法,使相同内容的对象其equals(Object obj)为真,返回的hashCode()一致。如下:

重写equals()方法
下面给出编写一个完美的equals方法的建议:

1) 显式参数命名为otherObject,稍后需要将它转换成另一个叫

做 other的变量。

2) 检测this与otherObject是否引用同一个对象:

        if (this == otherObject) return true;

        这条语句只是一个优化。实际上,这是一种经常采用的形

        式。因为计算这个等式要比一个一个地比较类中的域所付

        出的代价小得多。

3) 检测otherObject是否为null,如果为null,返回false。这项

      检测是很必要的。

                if (otherObject == null) return false;

      比较this与otherObject是否属于同一个类。如果equals的语

      义在每个子类中有所改变,就使用getClass检测:

              if (getClass() != otherObject.getClass()) return false;

      如果所有的子类都拥有统一的语义,就使用instanceof检测:

              if (! (otherObject instanceof ClassName)) retrun false;

4)将otherObject转换为相应的类类型变量:

        ClassName other = (ClassName)otherObject;

5) 现在开始对所有需要比较的域进行比较了。使用 == 比较

      基本类型域,使用equals比较对象域。如果所有的域都匹

      配,就返回ture;否则返回false。

              return field1 == other.field1

                        && field2.equals(other.field2)

                        && ……;

代码:

public boolean equals(Object obj) {        if (this == obj)            return true;        if (obj == null)            return false;        if (getClass() != obj.getClass())            return false;        Tree other = (Tree) obj;//Tree类        if (name == null) {            if (other.name != null)                return false;        } else if (!name.equals(other.name))            return false;        return true;    }        public int hashCode() {        final int prime = 31;        int result = 1;        result = prime * result + ((name == null) ? 0 : name.hashCode());        return result;    }

********************************************************************************************************************

关于如何重写hashCode的方法

为什么要重写hashCode方法?

 

我们应该先了解java判断两个对象是否相等的规则。

 

在java的集合中,判断两个对象是否相等的规则是:


首先,判断两个对象的hashCode是否相等

如果不相等,认为两个对象也不相等
如果相等,则判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等

 

我们在equals方法中需要向下转型,效率很低,所以先判断hashCode方法可以提高效率

 

如何重写hashCode方法呢?

 

你可以写

Java代码 复制代码
  1. public int hashCode(){   
  2.    return 42 ;   
  3. }  
public int hashCode(){
  return 42;
}

这是一种符合规则的写法,保证了两个equal 的object 拥有相同的hashCode

    但这种方法显然是不可取的

 

比较通用的做法是

返回一个result

 

Java代码 复制代码
  1. public int hashCode() {   
  2.    int result = 17 ;   //任意素数   
  3. result = 31 *result +c1; //c1,c2是什么看下文解释   
  4.    result = 31 *result +c2;   
  5.    return result;   
  6. }  
public int hashCode() {
  int result = 17;  //任意素数
 result = 31*result +c1; //c1,c2是什么看下文解释
  result = 31*result +c2;
  return result;
}

其中c1,c2是我们生成的你要计算在内的字段的代码,生成规则如下:

如果字段是boolean 计算为(f?1:0);

如果字段是byte,char,short,int则计算为 (int)f;

如果字段是long 计算为 (int)(f^(f>>32));

如果字段是float 计算为 Float.floatToLongBits(f);

如果字段是一个引用对象,那么直接调用对象的hashCode方法,如果需要判空,可以加上如果为空就返回0;
如果字段是一个数组则需要遍历所有元素,按上面几种方法计算;

 

当你写完后hashCode方法后问问自己

1、是否两个equal的实例,拥有相同的jhashCode

2、两个不同的实例,是否拥有相同的hashCode

写一个JUnit Test 测试一下

****************************************************************************************************************

01

一个重写equals ()和hashCode()方法的例子

下面是一个根据业务键实现equals ()与hashCode()的例子。实际中需要根据实际的需求,决定如何利用相关的业务键来组合以重写这两个方法。

 

Java代码 复制代码
  1. public class Cat   
  2. {   
  3.      private String name;   
  4.      private String birthday;   
  5.   
  6.      public Cat()   
  7.      {   
  8.      }   
  9.   
  10.      public void setName(String name)   
  11.      {   
  12.          this .name = name;   
  13.      }   
  14.   
  15.      public String getName()   
  16.      {   
  17.          return name;   
  18.      }   
  19.   
  20.      public void setBirthday(String birthday)   
  21.      {   
  22.          this .birthday = birthday;   
  23.      }   
  24.   
  25.      public String getBirthday()   
  26.      {   
  27.          return birthday;   
  28.      }   
  29.   
  30.         //重写 equals 方法   
  31.      public boolean equals (Object other)   
  32.      {   
  33.          if ( this == other)   
  34.          {   
  35.              //如果引用地址相同,即引用的是同一个对象,就返回true   
  36.              return true ;   
  37.          }   
  38.   
  39.              //如果other不是Cat类的实例,返回false   
  40.          if (!(other instanceOf Cat))   
  41.          {   
  42.              return false ;   
  43.          }   
  44.   
  45.          final Cat cat = (Cat)other;   
  46.              //name值不同,返回false   
  47.          if (!getName().equals (cat.getName())   
  48.              return false ;   
  49.              //birthday值不同,返回false   
  50.          if (!getBirthday().equals (cat.getBirthday()))   
  51.              return false ;   
  52.   
  53.          return true ;   
  54.      }   
  55.   
  56.         //重写hashCode()方法   
  57.      public int hashCode()   
  58.      {   
  59.          int result = getName().hashCode();   
  60.          result = 29 * result + getBirthday().hashCode();   
  61.          return result;   
  62.      }   
  63. }  
public class Cat 
{
 private String name;
 private String birthday;

 public Cat()
 {
 }

 public void setName(String name)
 {
  this.name = name;
 }

 public String getName()
 {
  return name;
 }

 public void setBirthday(String birthday)
 {
  this.birthday = birthday;
 }

 public String getBirthday()
 {
  return birthday;
 }

       //重写equals

方法
 public boolean equals

(Object other)
 {
  if(this == other)
  {
   //如果引用地址相同,即引用的是同一个对象,就返回true
   return true;
  }

            //如果other不是Cat类的实例,返回false
  if(!(other instanceOf Cat))
  {
   return false;
  }

  final Cat cat = (Cat)other;
            //name值不同,返回false
  if(!getName().equals

(cat.getName())
   return false;
            //birthday值不同,返回false
  if(!getBirthday().equals

(cat.getBirthday()))
   return false;

  return true;
 }

       //重写hashCode()方法
 public int hashCode()
 {
  int result = getName().hashCode();
  result = 29 * result + getBirthday().hashCode();
  return result;
 }
}

   重写父类方法的原则:可以重写方法的实现内容,成员的存取权限(只能扩大,不能缩小),或是成员的返回值类型(但此时子类的返回值类型必须是父类返回值类型的子类型)。

你可能感兴趣的:(java)