基本概念
hashcode:
有人说hashcode就是对象的内存地址,这种说法其实过于绝对,应该是根据不同的jvm实现决定的。 我理解hashcode的作用是返回哈希码,确定对象在hash表中的位置,所以仅仅当使用散列表的类【hashset, hashmap, hashtable】的时候才有重写的意义。如果是非散标的集合(比如list),就只需要重写equals了。如果不重写则返回Object中的默认实现。
equals:
说到equals,必须说下”==”,前者是比较对象的内容,后者是比较对象的地址。默认是采用Object中的equals方法,用“==“比较,即比较对象的地址。
举例说明:
后面就通过四个实例来验证以上的结论。
假设我们有一个Person类,如果名字相同我们就确认他们是同一个类。
- 1.不重写 hashcode和equals方法:
public class Person {
private String name;
private Integer age;
private String desc;
public Person(){
}
public Person(String name,Integer age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
public class TestHashCode {
public static void main(String[] args) {
Person p = new Person("张三", 12);
Person p2 = new Person("张三", 12);
System.err.println("==:"+(p == p2));
System.err.println("equals:"+(p.equals(p2)));
HashSet<Person> pSet = new HashSet<Person>();
pSet.add(p);
pSet.add(p2);
System.err.println("pSet: "+pSet.size());
List<Person> plist=new ArrayList<Person>();
plist.add(p);
plist.add(p2);
System.err.println("plist: "+plist.size());
}
}
执行结果:
==:false
equals:false
pSet: 2
plist: 2
- 2.重写equals方法:
public class Person {
private String name;
private Integer age;
private String desc;
public Person(){
}
public Person(String name,Integer age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class TestHashCode {
public static void main(String[] args) {
Person p = new Person("张三", 12);
Person p2 = new Person("张三", 12);
System.err.println("==:"+(p == p2));
System.err.println("equals:"+(p.equals(p2)));
HashSet<Person> pSet = new HashSet<Person>();
pSet.add(p);
pSet.add(p2);
System.err.println("pSet: "+pSet.size());
List<Person> plist=new ArrayList<Person>();
plist.add(p);
plist.add(p2);
System.err.println("plist: "+plist.size());
}
}
执行结果:
==:false
equals:true
pSet: 2
plist: 2
- 3.重写hashcode方法:
public class Person {
private String name;
private Integer age;
private String desc;
public Person(){
}
public Person(String name,Integer age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
public class TestHashCode {
public static void main(String[] args) {
Person p = new Person("张三", 12);
Person p2 = new Person("张三", 12);
System.err.println("==:"+(p == p2));
System.err.println("equals:"+(p.equals(p2)));
HashSet<Person> pSet = new HashSet<Person>();
pSet.add(p);
pSet.add(p2);
System.err.println("pSet: "+pSet.size());
List<Person> plist=new ArrayList<Person>();
plist.add(p);
plist.add(p2);
System.err.println("plist: "+plist.size());
}
}
执行结果:
==:false
equals:false
pSet: 2
plist: 2
- 4.重写equals和hashcode方法:
public class Person {
private String name;
private Integer age;
private String desc;
public Person(){
}
public Person(String name,Integer age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
public class TestHashCode {
public static void main(String[] args) {
Person p = new Person("张三", 12);
Person p2 = new Person("张三", 12);
System.err.println("==:"+(p == p2));
System.err.println("equals:"+(p.equals(p2)));
HashSet<Person> pSet = new HashSet<Person>();
pSet.add(p);
pSet.add(p2);
System.err.println("pSet: "+pSet.size());
List<Person> plist=new ArrayList<Person>();
plist.add(p);
plist.add(p2);
System.err.println("plist: "+plist.size());
}
}
执行结果:
==:false
equals:true
pSet: 1
plist: 2
- 结果分析:
我们可以看到,以上四种实验,验证了几下几个结论。
1.“==“”比较是内存地址; 未重写的对象equals也是比较的内存地址(Object类中的equals方法)
2.hashcode只是针对使用散列表的对象才有意义,例子中的list就没有什么意义,只需要重写equals就足够了。
3.对于需要用到散列表结构【比如示例中的hashSet】的对象,必须把hashcode以及equals一起重写,否则就会有潜藏的bug,见第二种情况,只是重写了equals,但是没有达到我们的目标。 因为对于hashset来说,首先判断对象的hashcode,如果hashcode不相等,则直接认为两对象不相等;如果hashcode相等,则再去判断equals。