Java开发之道
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

陷阱20 各负其责——重写equals方法但是没有重写hashCode方法

在程序开发的过程中,有时需要重写对象的equals方法,但是在重写对象的equals方法时,一定要记得重写对象的hashCode方法,因为在解决一些特殊问题时,需要应用对象的散列码,例如,当判断某个HashSet集合中是否包含某个对象时,就要用到对象的散列码,如果两个对象的散列码不同,就认为它们是不同的对象。

下面看一个由于重写对象的equals方法而没有重写hashCode方法,导致程序判断集合中是否包含指定对象出错的实例。

例3.20.1 创建类,在该类中重写equals 方法,然后创建该类的两个实例,将其中一个实例放到HashSet 集合中,并判断该集合中是否包含另一个对象。(光盘位置:光盘\MR\Instance\3\20\RewriteMethodApp)

    import java. util. HashSet;
    import java. util. Set;
    public class Book {
      private String bookType;              // 图书类型
      private String bookName;              // 图书名称
      public Book (String bookType, String bookName) {
          this. bookType = bookType;         // 初始化图书类型
          this. bookName = bookName;         // 初始化图书名称
      }
      public boolean equals (Object obj) {
          if (this == obj) {
            return true;                    // 当前对象与指定的对象相同返回true
          }
          if (obj == null) {
            return false;                   // 指定的对象为null,返回false
          }
          if (! (obj instanceof Book)) {
            return false;                   // 指定对象不是Book的实例,返回false
          }
          Book other = (Book) obj;          // 将指定对象转换为Book的实例
          if (bookName == null) {
            if (other. bookName ! = null) {
              // 如果图书名称是null, Book实例的图书名称不为null,返回false
              return false;
            }
          } else if (! bookName. equals (other. bookName)) {
            // 如果图书名称与Book实例的图书名称不同,返回false
            return false;
          }
          if (bookType == null) {
            if (other. bookType ! = null) {
              // 如果图书类型是null, Book实例的图书类型不为null,返回false
              return false;
            }
          } else if (! bookType. equals (other. bookType)) {
            // 如果图书类型与Book实例的图书类型不同,返回false
            return false;
          }
          return true;                        // 上述条件都不满足,说明两个对象相同,返回true
      }
      public static void main (String[] args) {
          Book book1 = new Book ("IT", "Java");  // 创建Book的实例book1
          Book book2 = new Book ("IT", "Java");  // 创建Book的实例book2
          // 输出两个对象的散列码
          System. out. println ("对象book1的散列码是:" + book1. hashCode ());
          System. out. println ("对象book2的散列码是:" + book2. hashCode ());
          // 使用HashSet类创建Set集合对象
      Set<Book> set = new HashSet<Book>();
          set. add (book1);                         // 将Book的实例book1添加到集合
          // 查看集合中是否包含Book的实例book2
      boolean bool = set. contains (book2);
          System. out. println ("Set集合中是否包含对象book2? " + bool);
      }
    }

运行本实例,程序将输出如图3.19所示的信息。

图3.19 没有重写hashCode方法输出的信息

说明

从图3.19的①和②可以看出,本实例输出Book类的两个实例book1和book2的散列码值是不同的,它们的散列码值分别为11077203和14576877,因此虽然book1和book2都是通过图书类型 "IT" 和图书名称 "Java" 创建的,但是由于它们的散列码值不同,所以从③处可以看出,在添加了book1的Set集合中判断是否包含book2时,输出了假值false,表示集合中不包含对象book2。

下面看一个在重写对象的equals方法的同时,也重写了hashCode方法,使程序能够正确判断集合中是否包含指定对象的实例。

例3.20.2 创建类,在该类中重写对象的equals方法和hashCode方法,然后创建该类的两个实例,将其中一个实例放到HashSet集合中,并判断该集合中是否包含另一个对象。(光盘位置:光盘\MR\ Instance\3\20\RewriteTwoMethodApp)

    import java. util. HashSet;
    import java. util. Set;
    public class Book {
      private String bookType;                // 图书类型
      private String bookName;                // 图书名称
      public Book (String bookType, String bookName) {
          this. bookType = bookType;           // 初始化图书类型
          this. bookName = bookName;           // 初始化图书名称
      }
      // 重写equals方法
      public boolean equals (Object obj) {
          if (this == obj) {
            return true;                      // 当前对象与指定的对象相同,返回true
          }
          if (obj == null) {
            return false;                     // 指定的对象为null,返回false
          }
          if (! (obj instanceof Book)) {
            return false;                     // 指定对象不是Book的实例,返回false
          }
          Book other = (Book) obj;            // 将指定对象转换为Book的实例
          if (bookName == null) {
            if (other. bookName ! = null) {
                // 如果图书名称是null, Book实例的图书名称不为null,返回false
                return false;
            }
          } else if (! bookName. equals (other. bookName)) {
            // 如果图书名称与Book实例的图书名称不同,返回false
            return false;
          }
          if (bookType == null) {
            if (other. bookType ! = null) {
                // 如果图书类型是null, Book实例的图书类型不为null,返回false
                return false;
            }
          } else if (! bookType. equals (other. bookType)) {
            // 如果图书类型与Book实例的图书类型不同,返回false
            return false;
          }
          return true;                        // 上述条件都不满足,说明两个对象相同,返回true
      }

运行本实例,由于重写equals方法的同时重写了hashCode方法,所以程序将输出如图3.20所示的信息。

图3.20 重写hashCode方法之后输出的信息

说明

从图3.20的①和②可以看出,Book 类的两个实例book1和book2的散列码值都是71349994,即它们的散列码值是相同的,因此对于通过图书类型 "IT" 和图书名称 "Java" 创建的两个对象book1和book2代表相同的对象,所以从图中的③处可以看出,在添加了book1的Set集合中判断是否包含book2时,输出了真值true,表示集合中包含了对象book2。