Java基礎系列-equals方法和hashCode方法

原創文章,轉載請標註出處:《Java基礎系列-equals方法和hashCode方法》html

概述

        equals方法和hashCode方法都是有Object類定義的。java

public class Object {
    public native int hashCode();
    public boolean equals(Object obj) {
        return (this == obj);
    }
}

        任何的類都是Object類的子類,全部它們默認都擁有這兩個方法。
        equals方法用於定義兩個對象的比較方式,而hashCode方法是native方法,主要用戶計算對象的hash值。算法

equals

        equals方法主要用於定義兩個對象的比較方式,默認的比較方式是比較內存地址,相對於基本類型來講就是值,而相對於引用類型來講就是堆中具體對象的地址。那麼就只有值相同的基本類型,和同一個對象的兩個引用才能相等。可是在咱們實際業務系統中,兩個對象的相等通常指的是兩個對象的內容相同(邏輯相同),而不是說它兩個是同一個對象,這種狀況使用默認的equals就沒法實現相等(由於兩個不一樣對象地址值必定不一樣),這時候咱們就須要對equals方法進行重寫,定義新的比較方式。數據結構

準則

  • 自省性:對於非null的x,存在:x.equals(x)返回true
  • 對稱性:對於非null的x和y,存在:x.equals(y)==y.equals(x)
  • 傳遞性:對於非null的x、y、z,存在:當x.equals(y)返回true,y.equals(z)返回true,則x.equals(z)必定爲true
  • 一致性:對於非null的x和y,屢次調用x.equals(y)所得的結果是不變的
  • 非空性:對於非null的x,存在x.equals(null)返回falseide

    重寫

            其實Java中已經爲咱們展現瞭如何重equals方法了,最經典的就是String的equals方法:
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    public boolean equals(Object anObject) {
        // 首先判斷兩個對象是否是同一個,地址相同否
        if (this == anObject) {
            return true;
        }
        // 判斷給定的對象是不是String類型,這裏instanceof關鍵字是重寫equals方法時常用的一個關鍵字
        // instanseof用於判斷右邊的類型是不是當前對象的類型或者超類型,超接口類型等
        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;
    }
}

        注意,使用instanceof在針對存在子類的狀況下,可能會出現違反對稱性和傳遞性的狀況,爲了不這種狀況,能夠通給getClass的方式比較類型。
        自定義重寫:函數

public class EqualsTest {
    private int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Override
    public boolean equals(Object obj) {
        // 知足非空性
        if(obj == null){
            return false;
        }
        // 知足自省性
        if(this == obj){
            return true;
        }
        // 知足對稱性、傳遞性、一致性
        if(this.getClass() == obj.getClass()
                && this.getClass().getClassLoader() == obj.getClass().getClassLoader()
                && this.id == ((EqualsTest)obj).getId()){
            return true;
        }
        return false;
    }
}

        注意:這裏若是是有不一樣的類加載器加載的同一類的實例也是沒法相等的。this

hashCode

        hashCode通常用於計算對象的hash值,它在類重寫equals的時候一塊兒重寫,重寫它的目的是爲了保證equals相同的兩個對象的hashCode結果一致,爲何要保證這一點呢,那就歸結到java中的那幾個基於Hash實現的集合上了,好比HashMap、HashSet等,這些集合須要用到對象的hash值來參與計算定位。
        使用hashCode的目的就是爲了散列元素,最終元素可否散列均勻和hashCode的實現息息相關,即爲hash函數。code

實現方式

  • 鏈地址法(理解):在出現hash衝突的時候,在這個位置再插入新元素,並與原有元素造成一個鏈表,相似於HashMap的實現方式
  • 開放尋址法(瞭解):在出現hash衝突的時候,在當前位置的附近尋找空位來存放新元素,這種方式只須要一種數據結構,不須要引入新的數據結構。其實就是爲每一個hash結果準備一個探查序列,用來存放發生hash衝突的元素。
    • 線性探查法:當出現hash衝突,則在當前位置逐個向後尋找空位,將新元素保存到找到的第一個空位,當找到最後時,須要折返到一開頭繼續查找。因爲探查序列固定,因此會引起一次集羣問題。
    • 二次探查法:出現衝突,再也不逐個順序探查,而是由某種函數計算的結果序列來探查,這個函數依賴於開始下標的平方,因此叫二次探查,開始下標的不一樣,序列就不相同,不一樣序列中會有重複的下標,因爲每一個下標開始的探查序列是固定的,因此會引起小規模集羣,即二次集羣問題。
    • 雙重散列法:要解決羣集,就要想辦法讓相同hash結果的序列不一樣,最好讓序列函數依賴於元素自己,保證當元素不一樣時,即便hash結果一致,但一旦發生衝突,不一樣的元素的序列是不一樣的(由於序列還要依賴元素自己,元素不一樣,序列結果就會不一樣),這樣存在兩個依賴變量的探查方法,能夠極大的避免集羣問題。
  • 再HASH法(知道)
  • 創建公共溢出區法(知道)

        hashCode的實現方式並非隨手而來的,須要考慮各類狀況,選擇合適的方式來實現,舉個例子,在Java的HashMap集合中,採用的就是鏈地址法來處理hash衝突。htm

        參考:對象

相關文章
相關標籤/搜索