equals方法和hashCode方法
在有些情况下,我们在设计一个类的时候,会重写equals 方法,比如String类,但是千万要注意,在重写equals 方法的同时,必须重写hashCode方法,为什么?
下面看一个例子:
package com.zd.test;
public class People {
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object paramObject) {
// TODO Auto-generated method stub
if(this == paramObject){
return true; //先检查是否其自反性,后比较paramObject是否为空。这样效率高
}
if(paramObject == null){
return false;
}
if(paramObject instanceof People){
People temp = (People)paramObject;
if(temp.getName().equals(this.name) && temp.getAge() == this.age){
return true;
}else{
return false;
}
}else{
return false;
}
}
}
package com.zd.test;
import java.util.HashMap;
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
People p1 = new People("jack", 12);
System.out.println(p1.hashCode());
HashMap<People, Integer> hashMap = new HashMap<People, Integer>();
hashMap.put(p1, 1);
System.out.println(hashMap.get(new People("jack", 12)));
}
}
在这里只重写了equals 方法,也就是说如果俩个People对象,如果他们姓名和年龄相等,则认为同一个人。
这段代码的意愿是输出为“1”,但是事实上他输出为 “null“。为什么?原因就在于重写equals方法的同时没有重写hashCode方法。
虽然通过重写equals方法使得逻辑上姓名和年龄相同的俩个对象为相等的对象,但是要知道默认情况下,hashCode方法是将对象的存储地址进行映射。那么上述代码输出为
”null“就不奇怪了。原因很简单,p1指向对象和
System.out.println(hashMap.get(new People("jack", 12)));
这句话中的 new People("Jack", 12) 不是同一个对象,他们的存储地址肯定不同。
下面是HashMap的get方法的具体实现:
public V get(Object paramObject)
{
if (paramObject == null) {
return getForNullKey();
}
Entry localEntry = getEntry(paramObject);
return null == localEntry ? null : localEntry.getValue();
}
所以在hashMap 进行get操作的时候,因为得到的hashcode值不同(注意:上述代码在某些情况下会得到相同的hashCode,不过这种概率比较小,因为虽然俩个对象的地
址不同,但是也有可能获得相同的hashCode),所以导致在get方法中for循环不会执行,直接返回null。
因此如果想要上述代码输出1 ,很简单,只要重写hashCode方法,让hashCode方法和equals 方法保持逻辑一致。
package com.zd.test;
public class People {
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return name.hashCode() * 37 + age;
}
@Override
public boolean equals(Object paramObject) {
// TODO Auto-generated method stub
if(this == paramObject){
return true; //先检查是否其自反性,后比较paramObject是否为空。这样效率高
}
if(paramObject == null){
return false;
}
if(paramObject instanceof People){
People temp = (People)paramObject;
if(temp.getName().equals(this.name) && temp.getAge() == this.age){
return true;
}else{
return false;
}
}else{
return false;
}
}
}
在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一的返回一个整数。
如果俩个对象根据equals 方法比较是相等的,则hashCode方法不一定得返回相同的整数;
如果 俩个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
设计hashCode时最重要的因素是:无论何时,对同一个对象调用hashCode都应该产生同样的值,如果在向一个Map中put时用的是一个值,而get的时候又是另一个值,那么
就无法获取到该对象了。