关注公众号“码农帮派”,查看更多系列技术文章:
我们知道equals()和hashCode()是java Object中两个基本方法,有时候由于业务的需求,需要我们重写equals()方法,比如对于Person类中,业务的需要让我们当Person对象的cardID一致的时候,就认为两个对象equals,此时就需要在Person类中重写equals()方法,如下:
public class Person { public String name; public int age; public String cardID; .....// 省略 @Override public boolean equals(Object o) { if (o instanceof Person) { Person p = (Person) o; return this.cardID.equals(p.cardID); } else { return false; } } @Override public int hashCode() { return this.cardID.hashCode(); } ......// 省略 }
阅读HashMap的源码,我们可以看到,HashMap中实现了一个Entry[]数组,数组的每个item是一个单项链表的结构,当我们put(key, value)的时候,HashMap首先会newItem.key.hashCode()作为该newItem在Entry[]中存储的下标,要是对应的下标的位置上没有任何item,则直接存储上去,要是已经有oldItem存储在了上面,那就会判断oldItem.key.equals(newItem.key),那么要是我们把上面的Person作为key进行存储的时候,重写了equals()方法,但没重写hashCode()方法,当我们去put()的时候,首先会通过hashCode() 计算下标,由于没有重写hashCode(),那么实质是完整的Object的hashCode(),会受到Object多个属性的影响,本来equals的两个Person对象,反而得到了两个不同的下标。
同样的,HashMap在get(key)的过程中,也是首先调用hashCode()计算item的下标,然后在对应下标的地方找,要是为null,就返回null,要是 != null,会去调用equals()方法,比较key是否一致,只有当key一致的时候,才会返回value,要是我们没有重写hashCode()方法,本来有的item,反而会找不到,返回null结果。
所以,要是你重写了equals()方法,而你的对象可能会放入到散列(HashMap,HashTable或HashSet等)中,那么还必须重写hashCode(), 如果你的对象有可能放到有序队列(实现了Comparable)中,那么还需要重写compareTo()的方法。
public class Person implements Comparable
{
public String name;
public int age;
public String cardID;
..... // 省略
@Override
public boolean equals(Object o)
{
if (o instanceof Person)
{
Person p = (Person) o;
return this.cardID.equals(p.cardID);
} else
{
return false;
}
}
@Override
public int hashCode()
{
return this.cardID.hashCode();
}
@Override
public int compareTo(Object another)
{
Person p = (Person) another;
return p.cardID.compareTo(this.cardID);
}
..... // 省略
}
[补充]
其实对于大多数情况下,我们都需要重写类的equals()方法:
public class Person
{
public String name;
public int age;
public String cardID;
}
对两个Person对象的equals()的结果:
public static void main(String[] args)
{
Person person = new Person();
person.name = "MXD";
person.age = 4;
person.cardID = "110";
Person person0 = new Person();
person0.name = "MXD";
person0.age = 4;
person0.cardID = "110";
System.out.println(person.equals(person0));
System.out.println("person的hashCode:" + person.hashCode());
System.out.println("person0的hashCode:" + person0.hashCode());
}
理论上应该返回true,但是:
这是因为,equals默认的比较的是person在内存中存储的位置,而不是内容是否相同,当我们对Person的equals()进行重写之后:
(下面是我们对Person类的equals方法以及hashCode方法进行重写之后的类):
public class Person
{
public String name;
public int age;
public String cardID;
@Override
public boolean equals(Object obj)
{
if (obj instanceof Person)
{
Person p = (Person) obj;
return this.cardID.equals(p.cardID) && this.name.equals(p.name) && this.age == p.age;
}else{
return false;
}
}
@Override
public int hashCode()
{
return this.cardID.hashCode();
}
}
针对之前相同的main()函数中的比较,得到的结果为:
我们发现,两个内容一致的Person对象的equals返回了true,而且hashCode也相同了。
[值得注意的是]
上面对hashCode()的重写中,只涉及到了cardID一项,更合理的是,所有属性的结合,比如各个属性的hashCode()再求异或,作为对象的hashCode值返回。