java 如何重寫equal 和hashcode方法(最佳實踐)


先看完理解這篇:Java hashCode() 和 equals()的若干問題解答

 

實現高質量的equals方法的訣竅包括  
  • 使用==操作符檢查“參數是否為這個對象的引用”;
  • 使用instanceof操作符檢查“參數是否為正確的類型”;
  • 對於類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;
  • 編寫完equals方法后,問自己它是否滿足對稱性、傳遞性、一致性;
  • 重寫equals時總是要重寫hashCode;
  • 不要將equals方法參數中的Object對象替換為其他的類型,在重寫時不要忘掉@Override注解。
public boolean equals(Object otherObject){       //測試兩個對象是否是同一個對象,是的話返回true
           if(this == otherObject) {  //測試檢測的對象是否為空,是就返回false
               return true;   
           } 
           if(otherObject == null) {  //測試兩個對象所屬的類是否相同,否則返回false
               return false;       
           }
           if(getClass() != otherObject.getClass()) {  //對otherObject進行類型轉換以便和類A的對象進行比較
               return false; 
           }       
           A other=(A)otherObject; 
           return Object.equals(類A對象的屬性A,other的屬性A)&&類A對象的屬性B==other的屬性B……;
    }  

例子:

public class TestEquals {

    public static void main(String[] args) {
        Person2 p1 = new Person2("aa", 13);
        Person2 p2 = new Person2("aa", 13);
        Person2 p3 = new Person2("bb", 13);
        System.out.println(p1.equals(p2)); // true
        System.out.println(p1.equals(p3)); // false
    }

}

class Person2 {
    private String name;
    private int age;
 
    public Person2(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public boolean equals(Object another) {
 
        //先判斷是不是自己,提高運行效率
        if (this == another)
            return true;
 
        //再判斷是不是Person類,提高代碼的健壯性
        if (another instanceof Person2) {
 
            //向下轉型,父類無法調用子類的成員和方法
            Person2 anotherPerson = (Person2) another;
 
            //最后判斷類的所有屬性是否相等,其中String類型和Object類型可以用相應的equals()來判斷
            if ((this.getName().equals(anotherPerson.getName())) && (this.getAge() == anotherPerson.getAge()))
                return true;
        } else {
            return false;
        }
 
        return false;
    }
}

 

實現hashCode方法的通用約定
  1. 在應用程序的執行期間,只要對象的equals方法的比較操作所用到的信息沒有被修改,那么對這個同一對象調用多次,hashCode方法必須始終如一地返回同一個整數。在同一個應用程序的多次執行過程中,每次執行所返回的整數可以不一致。

  2. 如果兩個對象根據equals(Object)方法比較是相等的,那么調用這兩個對象中任意一個對象的hashCode方法都必須產生同樣的整數結果。反之,如果兩個對象hashCode方法返回整數結果一樣,則不代表兩個對象相等,因為equals方法可以被重載。

  3. 如果兩個對象根據equals(Object)方法比較是不相等的,那么調用這兩個對象中任意一個對象的hashCode方法,則不一定要產生不同的整數結果。但,如果能讓不同的對象產生不同的整數結果,則有可能提高散列表的性能。

 

hashCode散列碼計算(來自:Effective Java)

  1. 把某個非零的常數值,比如17,保存在一個名為resultint類型的變量中。

  2. 對於對象中每個關鍵域f(equals方法中涉及的每個域),完成以下步驟:

    1. 為該域計算int類型的散列碼c:

      1. 如果該域是boolean類型,則計算(f?1:0)

      2. 如果該域是bytecharshort或者int類型,則計算(int)f

      3. 如果該域是long類型,則計算(int)(f^(f>>>32))

      4. 如果該域是float類型,則計算Float.floatToIntBits(f)

      5. 如果該域是double類型,則計算Double.doubleToLongBits(f),然后按照步驟2.1.3,為得到的long類型值計算散列值。

      6. 如果該域是一個對象引用,並且該類的equals方法通過遞歸地調用equals的方式來比較這個域,則同樣為這個域遞歸地調用hashCode。如果需要更復雜的比較,則為這個域計算一個范式(canonical representation),然后針對這個范式調用hashCode。如果這個域的值為null,則返回0(其他常數也行)。

      7. 如果該域是一個數組,則要把每一個元素當做單獨的域來處理。也就是說,遞歸地應用上述規則,對每個重要的元素計算一個散列碼,然后根據步驟2.2中的做法把這些散列值組合起來。如果數組域中的每個元素都很重要,可以利用發行版本1.5中增加的其中一個Arrays.hashCode方法。

    2. 按照下面的公式,把步驟2.1中計算得到的散列碼c合並到result中:result = 31 * result + c; //此處31是個奇素數,並且有個很好的特性,即用移位和減法來代替乘法,可以得到更好的性能:`31*i == (i<<5) - i, 現代JVM能自動完成此優化。

  3. 返回result

  4. 檢驗並測試該hashCode實現是否符合通用約定。

@Override
   public int hashCode() {
        int result = 17;
        result = 31 * result + mInt;
        result = 31 * result + (mBoolean ? 1 : 0);
        result = 31 * result + Float.floatToIntBits(mFloat);
        result = 31 * result + (int)(mLong ^ (mLong >>> 32));
        long mDoubleTemp = Double.doubleToLongBits(mDouble);
        result =31 * result + (int)(mDoubleTemp ^ (mDoubleTemp >>> 32));
        result = 31 * result + (mString == null ? 0 : mMsgContain.hashCode());
        result = 31 * result + (mObj == null ? 0 : mObj.hashCode());
        return result;
    }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM