程式員的成長之路
網際網路/程式員/技術/資料共享
JavaBean為什麼要重寫hashCode()方法和equals方法,我記得當時我巴拉巴拉半天就是沒有說到重點.
現在想一想歸根到底還是我對這兩個的了解不深刻,現在我特定來總結下.hashCode 方法用于散列集合的查找,equals 方法用于判斷兩個對象是否相等。
一、我們為什麼需要重寫hashCode()方法和equals()方法?(Why)
有時在我們的業務系統中判斷對象時有時候需要的不是一種嚴格意義上的相等,而是一種業務上的對象相等。在這種情況下,原生的equals方法就不能滿足我們的需求了.
我們所知道的JavaBean的超類(父類)是Object類,JavaBean中的equals方法是繼承自Object中的方法.Object類中定義的equals()方法是用來比較兩個引用所指向的對象的記憶體位址是否一緻.并不是比較兩個對象的屬性值是否一緻,是以這時我們需要重寫equals()方法.
Object類中equals()方法的源碼
public boolean equals(Object obj) {
return (this == obj);
}
public class Demo {
public static void main(String[] args) {
Student stu1 = new Student("awu",22);
Student stu2 = new Student("awu",22);
System.out.println(stu1.equals(stu2));
/*因為Student這個JavaBean沒有重寫關于屬性值相等的equals()方法
,是以預設比較的是位址值,進而輸出結果為false*/
}
}
那麼為什麼在重寫equals方法的時候需要重寫hashCode方法呢?
主要是Object.hashCode的通用約定:
- 在java應用程式運作時,無論何時多次調用同一個對象時的hsahCode()方法,這個對象的hashCode()方法的傳回值必須是相同的一個int值.
- 如果兩個對象equals()傳回值為true,則他們的hashCode()也必須傳回相同的int值.
- 如果兩個對象equals()傳回值為false,則他們的hashCode()傳回值也必須不同.
以HashSet來說明為什麼要這麼約定:HashSet存放元素時,根據元素的hashCode值快速找到要存儲的位置,如果這個位置有元素,兩個對象通過equals()比較,如果傳回值為true,則不放入;如果傳回值為false,則這個時候會以連結清單的形式在同一個位置上存放兩個元素,這會使得HashSet的性能降低,因為不能快速定位了。
還有一種情況就是兩個對象的hashCode()傳回值不同,但是equals()傳回true,這個時候HashSet會把這兩個對象都存進去,這就和Set集合不重複的規則相悖了;是以,我們重寫了equals()方法時,要按照b,c規則重寫hashCode()方法!(其實就是如果隻重寫了 equals 方法,兩個對象 equals 傳回了true,但是如果沒有重寫 hashCode 方法,集合還是會插入元素。這樣集合中就出現了重複元素了。)
二、在什麼情況下需要重寫hashCode()方法和equals()方法? (When)
當我們自定義的一個類,想要把它的執行個體儲存在以Hash散列查找的集合中時,我們就需要重寫這兩個方法;
public class Student {
private String name;
private Integer age;
public Student(){
}
public Student(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;
}
@Override
public int hashCode(){
final int prime = 31;
int result = 17;
result = prime * result + name.hashCode();
result = prime * result + age;
return result;
}
@Override
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj == null)
return false;
if(getClass() != obj.getClass())
return false;
final Student other = (Student)obj;
if(name.equals(other.name)){
return false;
}
if(age.equals(other.age)){
return false;
}
return true;
}
}
public class Demo {
public static void main(String[] args) {
Student stu1 = new Student("awu",22);
Student stu3 = new Student("awu",33);
Student stu2 = new Student("awu",22);
Set set = new HashSet();
set.add(stu1);
set.add(stu2);
set.add(stu3);
System.out.println(set.size());
/*輸出結果為2*/
}
}
如果不是以Hash散列查找的集合,即使重寫HashCode也沒多大實際用處.比如如下栗子:
public class Demo {
public static void main(String[] args) {
Student stu1 = new Student("awu",22);
Student stu3 = new Student("awu",33);
Student stu2 = new Student("awu",22);
ArrayList list = new ArrayList();
list.add(stu1);
list.add(stu2);
list.add(stu3);
System.out.println(list .size());
/*輸出結果為3*/
}
}
三、如何重寫這兩個方法?(How)
public class Student {
private String name;
private Integer age;
public Student(){
}
public Student(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;
}
@Override
public int hashCode(){
final int prime = 31;
int result = 17;
result = prime * result + name.hashCode();
result = prime * result + age;
return result;
}
@Override
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj == null)
return false;
if(getClass() != obj.getClass())
return false;
final Student other = (Student)obj;
if(name.equals(other.name)){
return false;
}
if(age.equals(other.age)){
return false;
}
return true;
}
}
<END>