以HashMap中的覆写为例,个人总结了一下hashCode()与equals()在程序运行中的作用和顺序的问题。
import java.util.Map ;
import java.util.HashMap ;
class Person{
private String name ;
private int age ;
public Person(String name,int age){
this.name = name ;
this.age = age ;
}
public String toString(){
return "姓名:" + this.name + ";年龄:" + this.age ;
}
public boolean equals(Object obj){ //覆写equals()方法
System.out.println("E"); //通过输出"E"与否判断是否调用equals()方法
if(this==obj){ //传入本类对象,返回true,表示是同一对象
return true ;
}
if(!(obj instanceof Person)){ //传入非本类对象,返回false,表示不是同一对象
return false ;
}
Person p = (Person)obj ; //对象向下转型
if(this.name.equals(p.name)&&this.age==p.age){ //各属性依次比较,相等返回true,不等返回false
return true ;
}else{
return false ;
}
}
public int hashCode(){ //覆写hashCode()方法
System.out.println("H"); //通过输出"H"与否判断是否调用hashCode()方法
return this.name.hashCode() * this.age ; //返回int值得计算公式,可以理解为对象hash值所对应的唯一存储地址
}
};
public class HashMapDemo{
public static void main(String args[]){
Map<Person,String> map = null ;
map = new HashMap<Person,String>() ;
map.put(new Person("张三",30),"zhangsan1"); // 增加内容
map.put(new Person("张三",30),"Zhangsan2");
map.put(new Person("李四",30),"lisi");
System.out.println(map.get(new Person("张三",30))) ;//查找内容
System.out.println(map.get(new Person("李四",30))) ;
}
};
程序运行结果:
H
H
E
H
H
E
Zhangsan2
H
E
lisi
将主程序中的代码适当删除,显示不同的运行结果,可以看出hashCode()与equals()究竟是怎么被调用的:
实验一:
map.put(new Person("张三",30),"zhangsan1"); // 增加内容
/*map.put(new Person("张三",30),"Zhangsan2");
map.put(new Person("李四",30),"lisi"); // */
System.out.println(map.get(new Person("张三",30))) ; //查找内容
/*System.out.println(map.get(new Person("李四",30))) ;*/
运行结果:
H
H
E
zhangsan1
结果分析:map.put()中创建了一个Person的匿名对象,然后在内部调用了hashCode()方法,得到它的hash值,没有与之冲突的hash值,则把对象属性放置在它的hash值对应的空间,map.get()则是通过键值来查找value,传入的键值是Person的匿名对象,需要得到它的地址才能返回对应的value值,这时匿名对象在内部调用hashCode()方法,得到一样的hash值,再由equals向下转型后判断出是同一个对象,然后也就由map.get()得到了对应的value值。
实验二:
map = new HashMap<Person,String>() ;
map.put(new Person("张三",30),"zhangsan1"); // 增加内容
map.put(new Person("张三",30),"Zhangsan2");
/*map.put(new Person("李四",30),"lisi"); // */
System.out.println(map.get(new Person("张三",30))) ; //查找内容
/*System.out.println(map.get(new Person("李四",30))) ;*/
运行结果:
H
H
E
H
E
Zhangsan2
结果分析:第一个"H"出现的原因与实验一中相同,然后第二次向map中增加内容,map.put()中再创建了一个Person的匿名对象,然后在内部调用了hashCode()方法,得到它的hash值,发现有与之冲突的hash值,即hash值相同,此时需要equals()方法来区分是否是同一个对象,对比发现是同一个对象,那么对于map.put()来说,即传入了相同的键值,由于HashMap操作时key值是不能重复的,如果重复则肯定会覆盖之前的内容,所以Zhangsan2覆写Zhangsan1存入它的hash值对应的空间,map.get()方法的运行过程如实验一分析,由于在第二次增加内容时value值被覆盖,所以显示的值变为Zhangsan2.
由上述两次实验也就可以的出源代码中的程序运行结果,第三次内容的增加和查找与实验一相同。
当用到集合框架的 Hashtable、HashMap、HashSet的时候,集合若要添加新的元素,先调用这个元素的hashCode()方法,定位到它应该放置的存储位置上。如果这个位置上没有元素,它就可以直接存储在 这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals()方法与新元素进行比较,相同的话就覆盖之前存入的内容,不相同就散列其它的地址。所以由于覆写hashCode()与equals()的作用结果,我们可以去掉集合中的相同元素,也可以通过匿名对象找到相应的value。