基本概念
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。