您的位置:首页 > 其它

覆写equals方法为什么需要覆写hashCode方法

2016-04-16 15:25 387 查看
  覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?

void test() {
// Person类的实例作为Map的key
Map<Person, Object> map = new HashMap<Person, Object>();
map.put(new Person("张三"), new Object());

// Person类的实例作为List的元素
List<Person> list = newArrayList<Person>()
list.add(new Person("张三"));

// 列表中是否包含
boolean b1 = list.contains(new Person("张三"));
// Map中是否包含
boolean b2 = map.containsKey(new Person("张三"));
System.out.println(b1 + "|" + b2);
}


Person类:

public class Person {
private String name;
private String age;

public Person(String name) {
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}

@Override
public boolean equals(Object o) {
/*
* 使用getClass是为了防止子类被判断为父类的情况
* 例如:Person中覆写equals方法 {return name.equals(p.getName);}
*     子类Employee继承Person,此时子类中的euqals方法super.equlas(p);
* 此时如果使用instanceof关键字,因为Employee是Person的子类,所以返回true
*  进一步调用equals返回的是true,所以会将Person和Employee认为是同一人,显然不合理
*/
if(o != null && o.getClass() == this.getClass()) {
Person p = (Person) o;
if(p.getName() == null && this.name == null)
return false;
return this.name.equals(p.getName());
}
return false;
}
}


  我们先来看b1,Person类的equals覆写了,不再判断两个地址是否相等,而是根据人员的姓名来判断两个对象是否相等,所以不管我们的 new Person(“张三”)产生了多少个对象,它们都是相等的。把“张三”对象放入List中,再检查List中是否包含,那结果肯定是true了。

  接着来看b2,我们把张三这个对象作为了Map的键(Key),放进去的对象是张三,检查的对象还是张三,那应该和List的结果相同了,但是很遗憾,结果是false。原因何在呢?

  原因就是HashMap的底层处理机制是以数组的方式保存Map条目(Map Entry)的,这其中的关键是这个数组下标的处理机制:依据传入元素hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了Map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到Map条目的链表中。同理,检查键是否存在也是根据哈希码确定位置,然后遍历查找键值的。

  接着深入探讨,那对象元素的hashCode方法返回的是什么值呢?它是一个对象的哈希码,是由Object类的本地方法生成的,确保每个对象有一个哈希码(这也是哈希算法的基本要求:任意输入k,通过一定算法f(k),将其转换为非可逆的输出,对于两个输入k1和k2,要求若k1=k2,则必须 f(k1)=f(k2),但也允许k1≠k2,f(k1)=f(k2)的情况存在)。

  那回到我们的例子上,由于我们没有重写hashCode方法,两个张三对象的hashCode方法返回值(也就是哈希码)肯定是不相同的了,在HashMap的数组中也就找不到对应的Map条目了,于是就返回了false。

  问题清楚了,修改也非常简单,重写一下hashCode方法即可,代码如下:

public int hashCode() {
//其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,
//使用起来非常方便,诸位可以直接在项目中集成。
return new HashCodeBuilder().append(name).toHashCode();
}


  其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,使用起来非常方便,诸位可以直接在项目中集成。(为什么不直接写hashCode方法?因为哈希码的生成有很多种算法,自己写麻烦,事儿又多,所以采用拿来主义是最好的方法。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: