相信有許多入門java的對于“==”和"equlas"一直處于懵懵懂懂的狀態,查了很多資料最終都混淆,這篇是本人回想兩者區別時候又陷入了懵懂狀態,故此重新對“==”和“equals”進行決議,若本章出現錯誤地方,請留言指正,謝謝!
1、原生equals與運算子“==”實質是一致的
為什么會說是一致性?那就需要從代碼的原生講起了;我們都知道"=="是java的元素運算子,而equals是Object類其中的一個方法,
“==”運算子在java中的作用應分為二種情況:
1、應用于基本資料型別【byte、short、int、long、float、double、char、boolean】,用于比較存盤的值是否相等【值的內容】,
2、應用于參考資料型別【類(class)、介面(interface)、陣列(array)】,用于比較所指向的物件地址是否相等,
“equals”是能用于比較參考型別,對于基本資料型別是沒有equals的,
從原始碼入手:
* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
* @see #hashCode()
* @see java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
}
從以上原始碼可以看出equals底層也是先進行“==”比較,在進行別的操作,也印證了上面所說的“==”和equals是沒有區別這句話;唯一的區別:是基本型別沒有equals方法【沒有繼承Object類】,也就是說基本資料型別只能使用“==”進行比較兩值是否相同,
2、理解“==”和“equals”在基本資料型別和參考資料型別的比較
在使用Java編程程序中,使用“equals”進行比較的操作,應用最多的應該是Striing類中的equals()方法,【String中的equals是重寫了Object中equals】
老規矩直接從原始碼入手:
1 * @param anObject 2 * The object to compare this {@code String} against 3 * 4 * @return {@code true} if the given object represents a {@code String} 5 * equivalent to this string, {@code false} otherwise 6 * 7 * @see #compareTo(String) 8 * @see #equalsIgnoreCase(String) 9 */ 10 public boolean equals(Object anObject) { 11 if (this == anObject) { 12 return true; 13 } 14 if (anObject instanceof String) { 15 String anotherString = (String)anObject; 16 int n = value.length; 17 if (n == anotherString.value.length) { //判斷兩個比較內容長度是否相等 18 char v1[] = value; 19 char v2[] = anotherString.value; 20 int i = 0; 21 while (n-- != 0) { //比較內容是否一致 22 if (v1[i] != v2[i]) 23 return false; 24 i++; 25 } 26 return true; 27 } 28 } 29 return false; 30 }
從原始碼可以看出,重寫的qulas還是一樣先判斷“==”也就是兩個物件所指向的地址是否相同,若相同則直接回傳true,若不相同在進行下一步比較,在回傳true,若兩個都不滿足則回傳false,
例如:
public static void main(String[] args) { String a = "Hello China"; String b = new String("Hello China"); String c = a; //值的傳遞,將a的內容傳遞給c //使用“==”和“equals”進行比較 //==對于參考資料型別比較的是指向的地址值是否相同 System.out.println(a == b);//false 地址不是指向同一個地方 System.out.println(a == c);//true 地址指向同一個地方 System.out.println(b == c);//false 地址不是指向同一個地方 //equals比較的是內容的值【先判斷==,==不滿足在判斷值內容】 System.out.println(a.equals(b));//true 地址指向的內容是否一致 System.out.println(a.equals(c));//true 地址指向內容是都一致 System.out.println(b.equals(c));//true 地址指向內容是否一致 }
看了上面的例子,或許你還是有點疑惑,為了更加方便理解我們直接上圖:


【紅色代表指向的地址,綠色表示指向的內容】另:因為String被final修飾的類所以指向常量池
綠色的線是通過new出來的一個新的Striing物件,當new出一個先的物件時候,該物件會現在常量池中找是否存在該物件,若不存在則在常量池中創建一個字串物件,然后將堆中該物件指向常量池中新創建的字串物件,
附:需注意String中的intern()方法;【intern() 回傳字串物件的規范化表示形式,】
public static void main(String[] args) { String a = "Hello China"; String b = new String("Hello China"); b=b.intern(); System.out.println(a == b); //true System.out.println(a.equals(b)); //true }
intern()方法是檢查字串池是否存在,如果存在則直接回傳true,若沒有加 [b = b.intern()]則a==b應該回傳false,加了intern()后當b在new一個新的物件前,會先到池里面查找是否存在,存在則直接指向就不在創建,故a==b也就指向了同一個地址,a==b就成立回傳true,
3、重寫equals的必要性
1、例如:我們新建一個Student類,new兩個Student物件,這兩個物件的姓名、年齡、性別相同,我們就認為兩個學生物件相等,但物件地址不一定相同,【未重寫時兩個物件地址不同,重寫后則相同】
1 public class Test { 2 public static void main(String[] args) { 3 4 //未重寫時候的地址不同 5 Student student1 = new Student("張三",20,"男"); 6 System.out.println(student1);//@266474c2 7 Student student2 = new Student("張三",20,"男"); 8 System.out.println(student2);//@6f94fa3e 9 System.out.println(student1.equals(student2));//未重寫equals時為 false 10 11 12 Student student1 = new Student("張三",20,"男"); 13 System.out.println(student1);//@2c63a8ab 14 Student student2 = new Student("張三",20,"男"); 15 System.out.println(student2);//@2c63a8ab 16 System.out.println(student1.equals(student2));//重寫equals后為 true 17 18 } 19 } 20 21 22 class Student{ 23 public String name; 24 public int age; 25 public String sex; 26 27 28 29 30 @Override 31 public int hashCode() { 32 final int prime = 31; 33 int result = 1; 34 result = prime * result + age; 35 result = prime * result + ((name == null) ? 0 : name.hashCode()); 36 result = prime * result + ((sex == null) ? 0 : sex.hashCode()); 37 return result; 38 } 39 40 42 43 @Override 44 public boolean equals(Object obj) { 45 if (this == obj) 46 return true; 47 if (obj == null) 48 return false; 49 if (getClass() != obj.getClass()) 50 return false; 51 Student other = (Student) obj; 52 if (age != other.age) 53 return false; 54 if (name == null) { 55 if (other.name != null) 56 return false; 57 } else if (!name.equals(other.name)) 58 return false; 59 if (sex == null) { 60 if (other.sex != null) 61 return false; 62 } else if (!sex.equals(other.sex)) 63 return false; 64 return true; 65 } 66 67 68 69 70 public Student(String name, int age, String sex) { 71 this.name = name; 72 this.age = age; 73 this.sex = sex; 74 } 75 }
重寫equals大致是先判斷是都是同一個物件,然后在依次比較物件內的name,age,sex值,【因為String類內部已經重寫過equals所以直接使用equals就可以比較內容】
日常開發中,一般是要重寫equals才符合實際生活情況!
但是隨著重寫equals又出現了新的問題,為什么還要重寫hashCode,不重寫會怎么樣!!!
4、重寫HashCode
hashcode方法是根據物件的地址轉換之后回傳的一個哈希值,使用hashcode方法,會回傳一個哈希值,哈希值對陣列的長度取余后會確定一個存盤的下標位置,不同的哈希值取余之后的結果可能是相同的,相同的時候就用equals方法判斷是否為相同的物件,不同則在鏈表中插入,若哈希值取余后不相同,則插入的位置也不同,兩個物件肯定不相同,【結合圖解】

小結:
經過上面描述后,在判斷的時先根據hashcode進行的判斷,相同的情況下再根據equals()方法進行判斷,如果只重寫了equals方法,而不重寫hashcode的方法,會造成hashcode的值不同,而equals()方法判斷出來的結果為true,【地址不同情況下也回傳true】,而我們需要的是兩個物件在相同情況下在進行equals判斷,因此重寫hashCode是避免地址值不相同情況下也進行覆寫,
為了更進一步理解,這邊使用HashMap的鍵值對來進行測驗【HashMap的鍵不能重復,底層基于陣列+鏈表+紅黑樹結構】==》鏈表長度大于8時候轉換為紅黑樹
1、不重寫hashCode()方法:
1 public class Test { 2 public static void main(String[] args) { 3 Student student1 = new Student("張三",20,"男"); 4 System.out.println("student1的hashCode值:"+student1.hashCode());// 5 Student student2 = new Student("張三",20,"男"); 6 System.out.println("student1的hashCode值:"+student2.hashCode());// 7 System.out.println("比較兩個物件是否相等:"+student1.equals(student2));//未重寫equals時為 false 8 HashMap<Student,Integer> studentMap = new HashMap(); 9 studentMap.put(student1,111); 10 studentMap.put(student2,222); 11 System.out.println("當前的map大小:"+studentMap.size());// 12 13 } 14 }
輸出結果:

2、重寫hashCode()方法:
輸出結果:

很明顯,在沒有重寫hashCoede()的時候,他們的hash值不同,也就是存盤在陣列中的位置不同,但因為重寫了equals(),所以內容是一致的,但在不能出現重復鍵的Map中,他們還是沒有進行覆寫,顯然這不符合實際邏輯,
在重寫hashCode()之后,在進行map存盤時候會先判斷hashCode值是否相同,相同則指向存盤陣列中的同一個位置,在進行內容判斷,內容相同才進行覆寫操作,故回傳的size為1,若不相同則使用里鏈表方式,存放于后面,
小結:
1、不重寫hashCode跟equals時候回傳肯定為false,因為原生Object的equals判斷的是地址值【==】,
2、不重寫hashCode,重寫equals回傳的是true,在進行存盤時指向的位置下標不同,但由于重寫判斷內容,所以使用equals判斷內容時候會回傳true,
3、重寫hashCoe,不重寫equals回傳false,因為兩個物件的地址相同了,但未重寫比較內容,因此回傳false【鏈表情況】,
4、重寫了hashCode,重寫了equals回傳true,這個時候在map中指向的陣列下標一致,內容也一致,就會產生覆寫,
【簡單來說:重寫hashCode()是為了讓兩者在記憶體中指向同一個地方,而重寫equals()是為了在指向同一個地方同時判斷是都完全一致】
若有不但之處,歡迎指正!一起學習!!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/149592.html
標籤:其他
上一篇:開源兩個spring api專案
