「==」和「equlas」區別【詳解】

相信有許多入門java的對於「==」和"equlas"一直處於懵懵懂懂的狀態,查了不少資料最終都混淆。這篇是本人回想二者區別時候又陷入了懵懂狀態,故此從新對「==」和「equals」進行解析,若本章出現錯誤地方,請留言指正,謝謝!

一、原生equals與運算符「==」實質是一致的

爲何會說是一致性?那就須要從代碼的原生講起了;咱們都知道"=="是java的元素運算符,而equals是Object類其中的一個方法。java

  「==」運算符在java中的做用應分爲二種狀況:編程

    一、應用於基本數據類型【byte、short、int、long、float、double、char、boolean】,用於比較存儲的值是否相等【值的內容】。數組

    二、應用於引用數據類型【類(class)、接口(interface)、數組(array)】,用於比較所指向的對象地址是否相等。ide

  「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類】,也就是說基本數據類型只能使用「==」進行比較兩值是否相同。ui

二、理解「==」和「equals」在基本數據類型和引用數據類型的比較

在使用Java編程過程當中,使用「equals」進行比較的操做,應用最多的應該是Striing類中的equals()方法。【String中的equals是重寫了Object中equals】this

老規矩直接從源碼入手:spa

 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。code

例如:

 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。

三、重寫equals的必要性

一、例如:咱們新建一個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,不重寫會怎麼樣!!!

四、重寫HashCode

hashcode方法是根據對象的地址轉換以後返回的一個哈希值,使用hashcode方法,會返回一個哈希值,哈希值對數組的長度取餘後會肯定一個存儲的下標位置。不一樣的哈希值取餘以後的結果多是相同的,相同的時候就用equals方法判斷是否爲相同的對象,不一樣則在鏈表中插入。若哈希值取餘後不相同,則插入的位置也不一樣,兩個對象確定不相同。【結合圖解】

 

 

 小結:

   通過上面描述後,在判斷的時先根據hashcode進行的判斷,相同的狀況下再根據equals()方法進行判斷。若是隻重寫了equals方法,而不重寫hashcode的方法,會形成hashcode的值不一樣,而equals()方法判斷出來的結果爲true。【地址不一樣狀況下也返回true】。而咱們須要的是兩個對象在相同狀況下在進行equals判斷,所以重寫hashCode是避免地址值不相同狀況下也進行覆蓋。

爲了更進一步理解,這邊使用HashMap的鍵值對來進行測試【HashMap的鍵不能重複,底層基於數組+鏈表+紅黑樹結構】==》鏈表長度大於8時候轉換爲紅黑樹

一、不重寫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 }

 

輸出結果:

 

二、重寫hashCode()方法:

輸出結果:

很明顯,在沒有重寫hashCoede()的時候,他們的hash值不一樣,也就是存儲在數組中的位置不一樣,但由於重寫了equals(),因此內容是一致的。但在不能出現重複鍵的Map中,他們仍是沒有進行覆蓋,顯然這不符合實際邏輯。

在重寫hashCode()以後,在進行map存儲時候會先判斷hashCode值是否相同,相同則指向存儲數組中的同一個位置,在進行內容判斷,內容相同才進行覆蓋操做,故返回的size爲1,若不相同則使用裏鏈表方式,存放於後面。

小結:

 

 

 一、不重寫hashCode跟equals時候返回確定爲false,由於原生Object的equals判斷的是地址值【==】。

二、不重寫hashCode,重寫equals返回的是true,在進行存儲時指向的位置下標不一樣,但因爲重寫判斷內容,因此使用equals判斷內容時候會返回true。

三、重寫hashCoe,不重寫equals返回false,由於兩個對象的地址相同了,但未重寫比較內容,所以返回false【鏈表狀況】。

四、重寫了hashCode,重寫了equals返回true,這個時候在map中指向的數組下標一致,內容也一致,就會產生覆蓋。

【簡單來講:重寫hashCode()是爲了讓二者在內存中指向同一個地方,而重寫equals()是爲了在指向同一個地方同時判斷是都徹底一致】

如有不但之處,歡迎指正!一塊兒學習!!!!

相關文章
相關標籤/搜索