一 :string類型的==和equals的區別:
結論:"=="是判斷兩個字符串的內存地址是否相等,equals是比較兩個字符串的值是否相等,具體就不做擴展了,有興趣的同學可以去查看相關的博客。
String s1 = new String("java");
String s2 = new String("java");
System.out.println(s1==s2); //false
System.out.println(s1.equals(s2)); //true
在Java中,equals和hashCode方法是Object中提供的兩個方法,string類型重寫了equals()方法和hashCod()方法,源碼如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
/*返回哈希碼,String的哈希碼計算方式為s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]*/
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
二:對象equals 和hashcode的關系
結論:當對象的equals()方法被重寫時,通常有必要重寫 hashCode() 方法,以維護 hashCode 方法的常規協定,該協定聲明相等對象必須具有相等的哈希碼。如下:
(1)兩個對象相等,hashcode一定相等
(2)兩個對象不等,hashcode不一定不等
(3)hashcode相等,兩個對象不一定相等
(4)hashcode不等,兩個對象一定不等
hashcode是用於散列數據的快速存取,如利用HashSet/HashMap/Hashtable類來存儲數據時,都是根據存儲對象的hashcode值來進行判斷是否相同的。這樣如果我們對一個對象重寫了euqals,意思是只要對象的成員變量值都相等那么euqals就等於true,但 不重寫hashcode,那么我們再new一個新的對象,當原對象.equals(新對象)等於true時,兩者的hashcode卻是不一樣的,由此將產生了理解的不一致。
三 :使用HashMap,如果key是自定義的類,就必須重寫hashcode()和equals()
A.當put元素時:
1.首先根據put元素的key獲取hashcode,然后根據hashcode算出數組的下標位置,如果下標位置沒有元素,直接放入元素即可。
2.如果該下標位置有元素(即根據put元素的key算出的hashcode一樣即重復了),則需要已有元素和put元素的key對象比較equals方法,如果equals不一樣,則說明可以放入進map中。這里由於hashcode一樣,所以得出的數組下標位置相同。所以會在該 數組位置創建一個鏈表,后put進入的元素到放鏈表頭,原來的元素向后移動。
B.當get元素時:
根據元素的key獲取hashcode,然后根據hashcode獲取數組下標位置,如果只有一個元素則直接取出。如果該位置一個鏈表,則需要調用equals方法遍歷鏈表中的所有元素與當前的元素比較,得到真正想要的對象。
C. 采用hashcode的好處:
如果我們不使用hashcode,直接使用equals比較,那么我們有10w個元素,put值或者get值時要比較10w次,如果先通過hashcode判斷后,我們的效率就會提高很多。
我們可以對比實際生活中,如果有一棟教學樓,一樓是1年級,二樓是2年級,以此類推,六樓是6年級,這時我們需要找5年級2班在哪間教室,通過hashcode直接定位到5樓,然后在5樓一間間找就行了,這樣避免從一樓一間一間查看。
四.:代碼展示,為什么重寫equals時必須重寫hashCode方法
我們以一個student 學生類來測試重寫equals()和重寫hashcode()后,map的get()方法返回值結果,讓大家清晰了解為什么重寫equals時必須重寫hashCode方法
package com.sinaif;
public class Student {
private String name; //姓名
private String sex; //性別
private String idCard;//身份證號
Student(String name,String sex,String idCard){
this.name = name;
this.sex = sex;
this.idCard = idCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj == null){
return false;
}
Student obj1 = (Student)obj;
if(this.name.equals(obj1.getName()) && this.idCard.equals(obj1.getIdCard())){
return true;
}
return false;
}
//@Override
public int hashCode() {
// TODO Auto-generated method stub
String s= this.name + this.idCard;
return s.hashCode();
}
}
public static void main(String[] args) throws Exception {
Map<Student,Student> map = new HashMap<Student,Student>();
Student s1 = new Student("haly","male","110200");
Student s2 = new Student("haly","female","110200");
map.put(s1, s1);
System.out.println(map.get(s2));
// 當沒有重寫equals方法和hashcode方法,打印出null
// 當重寫equals方法,不重寫hashcode方法時,打印出null
// 當重寫equals方法,並且重寫hashcode方法時,打印出com.test.Student@95fb7d6
}
