深入淺出hashCode()和equals

一、目的

   經典面試題,自己動手剖析,用於個人記錄共同學習。

二、equals簡介

1.equals和==區別

1.==

String str1 = "string";
String str2 = new String("string");
boolean b = str1 == str2;//false

這裏str1==str2比較的是對象的引用地址是否指向的是同一個對象。

 == 比較的是變量(棧)內存中存放的對象的(堆)內存地址,用來判斷兩個對象的地址是否相同,即是否是指相同一個對象。比較的是真正意義上的指針操作

引用地址:

public class TestDemo {
    public static void main(String[] args) {
        String str1 = "string";
        String str2 = new String("string");
        System.out.println(toString(str1));//java.lang.String@914504136
        System.out.println(toString(str2));//java.lang.String@166239592
    }
    public static String toString(String str) {
        return str.getClass().getName() + "@" + System.identityHashCode(str);
    }
}

2.equals

先看一下Object的equals方法

public boolean equals(Object obj) {
   return (this == obj);
}

Object的equals方法內部也是用==比較的引用地址。

再看一下String的equals方法

public boolean equals(Object anObject) {
    //驗證引用地址是否相同
    if (this == anObject) {
        return true;
    }
    //驗證類型是否相同
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        //驗證長度是否相同
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            //對比每個字符是否相同
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

可以看到String重寫的equals方法進行對比。

小結:equals()和 == 有着本質的區別,== 可以看作是對“操作系統比較數據手段”的封裝,而equals()則是每個對象自帶的比較方法,它是Java自定義的比較規則。

三、hashCode簡介

hashCode是哈希值,哈希值是通過哈希函數計算得來的。

哈希函數能夠保證相同的輸入能夠得到相同的輸出(哈希值),但是不能夠保證不同的輸入總是能得出不同的輸出。

hashCode的存在主要是用於查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結構中確定對象的存儲地址的。

四、淺談hashCode和equals

Map和Set集合都是不允許元素重複的,嚴格來說Map存儲的是鍵值對,它不允許重複的鍵值。

這些容器在存儲元素的時必須對元素做出判斷:在當前的容器中有沒有和新元素相同的元素

Map 和 Set 的絕大多數實現類的底層都會用到散列表結構。

如果直接用equals判斷重複,時間複雜度爲O(n),但在散列表的基礎上,判斷是否重複就容易得多了。

我們不妨假設兩個相同的對象,hashCode() 一定相同,這麼一來就體現出哈希函數的威力了。

由於相同的輸入一定會產生相同的輸出,於是如果新對象,和容器中已存在的對象相同,新對象計算出的哈希值就會和已存在的對象的哈希值產生衝突。這時容器就能判斷:這個新加入的元素已經存在,需要另作處理:覆蓋掉原來的元素(key)或捨棄。

按照這個思路,如果這個元素計算出的哈希值所對應的內存單元沒有產生衝突,也就是沒有重複的元素,那麼它就可以直接插入。所以當運用 hashCode() 時,判斷是否有相同元素的代價,只是一次哈希計算,時間複雜度爲O(1),這極大地提高了數據的存儲性能。

五、hashCode和equals關係

不同的參數可能會輸出相同的哈希值,也就是形成哈希衝突,這樣一來就需要equals進行二次判斷是否相同。

這也是爲什麼 Java 官方推薦我們在一個類中,最好同時重寫 hashCode() 和 equals() 方法的原因。

hashCode和equals規則

  • 如果兩個對象是相等的,它們的 equals() 方法應該要返回 true,它們的 hashCode() 需要返回相同的結果;
  • 如果equals方法得到的結果爲false,則兩個對象的hashcode值不一定不同;
  • 如果兩個對象的hashcode值不等,則equals方法得到的結果必定爲false;
  • 如果兩個對象的hashcode值相等,則equals方法得到的結果不一定相等;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章