为何equals(Object o)相等,hashCode()必须相等

首先,int hashCode();是为了支持哈希表类的如HashMapHashTable之类的底层使用了哈希表的类。java

Java Object规范中int hashCode()方法的约定类容有三个:数组

(1)              只要对象的equals方法所用到的信息没有修改,那么hashCode方法必须始终如一的返回一个同一整数,在同一应用程序中屡次执行中,每一次执行能够不同。数据结构

(2)              若是两个对象的equals方法想到,那么每个对象单独调用hashCode返回的数字必须相等。ide

(3)              若是两个对象的equals方法不相等,hashCode方法的返回值能够相等,给不一样的对象返回不一样的值能够提升哈希表的性能。函数

下面咱们以HashMap为例来看一看为何要作这样的约定。性能

下图是HashMap的底层数据结构:this

图像来自http://zhangshixi.iteye.com/blog/672697spa

经过源码和上面的示意图咱们能够知道HashMap的底层是一个数组,数组的每个元素是一个Entry组织的链表。下面是HashMappublic V put(K key, V value)方法的源代码(1.7):code

 

咱们观察到被标记的两条语句,首先是经过hash(Object key)方法获得一个hash值,而后经过indexFor方法定位到hash值在数组中的位置。下面是hash(Object key)的源码。咱们能够看出它调用了KeyhashCode()函数。对象

int indexFor(int h, int length)的源码:
  static int indexFor(int h, int length) {
        return h & (length-1);
    }


从上面的内容咱们知道咱们用HashMap存储时是和Keyhash值相关的。若是hash值相同,那么定位数组的位置也相同(由于indexFor的返回值只和hash值和数组长度length有关,而数组的length只会在重散列时变化)

从前面咱们知道数组的每个元素都是Entry的链表,若是每一次hash值都相同,那么每一次都定位到数组的相同位置,那么链表就会很长,处理的时间也会很长。

hash值是和KeyhashCode方法相关的,因此咱们就能够理解hashCode的第三条约定了,给不一样对象产生不一样的hashCode能够提升哈希表的性能。

咱们如今再来看一看为何若是两个对象的equals方法相等,那么每个对象单独调用hashCode方法必须返回相同的返回值。首先咱们仍是先看一看HashMap取得的源代码:

public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);
 
        return null == entry ? null : entry.getValue();
    }

 

咱们能够发现是可逆的,仍是经过hash方法获得hash值,再经过indexFor方法定位元素在数组中的位置。想想若是咱们两个对象的equals方法相等,而hashCode方法的值能够不相等,那是否是就意味着两个逻辑上相同的对象能够放在不一样的位置。那么是否是就意味着咱们经过逻辑上相等的Key查找的是不一样的地址(对象)。虽然这并无什么错误,可是并非咱们实现HashMap的目的。下面经过一个例子简单的看一看会出现什么问题。

public class User
{
       private long id;
       private String name;
       private String address;
       private long phone;
       @Override
       public boolean equals(Object obj)
       {
              if(obj == this)
                     return true;
              if(! (obj instanceof User))
                     return false;
              User user = (User) obj;
              return (user.id == id && user.phone == phone && name.equals(user.name) && address.equals(user.address));
       }
       
       public User(long id, String name, String address, long phone)
       {
              super();
              this.id = id;
              this.name = name;
              this.address = address;
              this.phone = phone;
       }
 
       public long getId()
       {
              return id;
       }
       public void setId(long id)
       {
              this.id = id;
       }
       public String getName()
       {
              return name;
       }
       public void setName(String name)
       {
              this.name = name;
       }
       public String getAddress()
       {
              return address;
       }
       public void setAddress(String address)
       {
              this.address = address;
       }
       public long getPhone()
       {
              return phone;
       }
       public void setPhone(long phone)
       {
              this.phone = phone;
       }
       
       public static void main(String[] args)
       {
              HashMap<User, String> map = new HashMap<User,String>();
              map.put(new User(1,"tom","china",13888888888L),"hello world");
              System.out.println(map.get(new User(1,"tom","china",13888888888L)));     
       }      
}

咱们的本意是经过User来查找hello world,可是并无如咱们所愿对吗?为何呢?由于咱们重写了Userequals方法,可是没有重写hashCode方法,因此用的是继承自Object类的hashCode方法。由于是用new因此地址并不同,hashCode的值天然也就不相同了。因此定位到了其余的位置,什么都没有找到返回null

相关文章
相关标签/搜索