Java常見面試問題: equals()與hashCode()的使用

1 equals()與‘==’的區別

默認狀況下也就是從超類Object繼承而來的equals()方法與‘==’是徹底等價的, 比較的都是對象的內存地址.java

但咱們能夠重寫equals()方法, 使其按照咱們的需求的方式進行比較, 好比String類就重寫了equals方法, 它比較的是字符的序列, 而再也不是內存地址.api

2 equals()方法的重寫規則

自反性、對稱性、傳遞性等都是 <集合論> 中的概念. 數組

(1) 自反性: 對於任何非null的引用值x, x.equals(x)應返回true.安全

(2) 對稱性: 對於任何非null的引用值x與y, 當且僅當:y.equals(x)返回true時, x.equals(y)才返回true.ide

(3) 傳遞性: 對於任何非null的引用值x、y與z, 若是y.equals(x)返回true, y.equals(z)返回true, 那麼x.equals(z)也應返回true.性能

(4) 一致性: 對於任何非null的引用值x與y, 假設對象上equals比較中的信息沒有被修改, 則屢次調用x.equals(y)始終返回true或者始終返回false.this

(5) 對於任何非空引用值x, x.equal(null)應返回false..net

3 爲何重寫equals()的同時還須要重寫hashCode()

這個問題主要和映射(Map接口)相關. 咱們知道Map接口的類會使用到鍵(Key)的哈希碼, 當咱們調用put()/get()方法操做Map容器時, 都是根據Key的哈希碼來計算存儲位置的, 所以若是咱們對哈希碼的獲取沒有相關保證, 就可能會得不到預期的結果.code

在Java中, 咱們能夠經過hashCode()方法獲取對象的哈希碼, 哈希碼的值就是對象的存儲地址, 這個方法在Object類中聲明, 所以全部的子類都含有該方法.

hashCode就是哈希碼(或者散列碼), 是由對象導出的一個整型值, 哈希碼是沒有規律的, 若是x與y是兩個不一樣的對象, 那麼x.hashCode()與y.hashCode()就不會相同 —— 只要x和y對象沒有重寫hashCode()方法, JVM規範中明確要求它們的散列碼不會相同.

String(字符串)的哈希碼是由字符串的內容導出的, 也就是String類中重寫了hashCode()方法.

4 JDK 7中對hashCode()方法的改進

(1) Java發佈者但願咱們使用更加安全的調用方式來返回散列碼, 也就是使用null安全的java.util.Objects.hashCode()方法, 這個方法的優勢是若是參數爲null, 就只返回0, 不然返回對象參數調用的hashCode的結果.

Objects.hashCode()的源碼以下:

public static int hashCode(Object o) {
    return o != null ? o.hashCode() : 0;
}

(2) JDK 7中還提供了一個方法: java.util.Objects.hash(Object... objects), 當須要組合多個散列值時能夠調用該方法, 好比:

public class Model {
    private String name;
    private double salary;
    private int sex;
    // @Override
    // public int hashCode() {
    //     return Objects.hashCode(name) + new Double(salary).hashCode() + new Integer(sex).hashCode();
    // }

    @Override
    public int hashCode() {
        return Objects.hash(name, salary, sex);
    }
}

擴展: 若是咱們提供的是一個數值類型的變量, 那麼咱們能夠調用Arrays.hashCode()方法來計算它的散列碼, 這個散列碼是由數組中各個元素的散列碼組成的.

5 Java API文檔中關於hashCode()方法的規定

—— 內容摘自《Java深刻解析》.

(1) 在Java應用程序執行期間, 若是在equals()方法中涉及到的信息沒有被修改, 那麼在同一個對象上屢次調用hashCode()方法時必須一致地返回相同的整數. 若是屢次執行同一個應用程序時, 不要求該整數必須相同.

(2) 若是兩個對象經過調用equals()方法是相等的, 那麼這兩個對象調用hashCode()方法必須返回相同的整數.

(3) 若是兩個對象經過調用equals()方法是不相等的, 不要求這兩個對象調用hashCode()方法必須返回不一樣的整數. 可是開發人員應該意識到: 對不一樣的對象產生不一樣的hash值能夠提升哈希表的性能.

6 重寫equals()方法時推薦使用getClass(), 而不是instanceof

在重寫equals()方法時, 通常推薦使用getClass()來進行類型判斷, 而不是使用instanceof關鍵字.

除非全部的子類有統一的語義才使用instanceof, 統一的語義就是說, 不一樣的子類在equals()方法中比較的內容相同.

咱們知道, instanceof關鍵字的做用是判斷其左邊對象是否爲其右邊類型的實例, 返回boolean類型的數據, 它多用來判斷繼承關係中的某個子類的實例是否爲父類的實現.

7 編寫一個完美的equals()方法的建議

—— 摘自《Java核心技術 第一卷:基礎知識》.

(1) 顯式參數命名爲otherObject, 稍後須要將它轉換成另外一個叫作other的變量 (參數名命名, 強制轉換請參考下一條建議);

(2) 將otherObject轉換爲相應的類類型變量: ClassName other = (ClassName) otherObject;;

(3) 檢測this與otherObject是否引用同一個對象: if(this == otherObject) return true; —— 存儲地址相同, 確定是同個對象, 直接返回true;

(4) 檢測otherObject是否爲null , 若是爲null, 返回false: if(otherObject == null) return false;;

(5) 比較this與otherObject是否屬於同一個類(視需求而選擇):

① 若是equals的語義(能夠理解爲equals比較的內容)在每一個子類中有所改變, 就使用getClass檢測: if(getClass() != otherObject.getClass()) return false;

② 若是全部的子類都擁有統一的語義(比較的內容不變), 就使用instanceof檢測: if(!(otherObject instanceof ClassName)) return false;

(6) 對全部須要比較的域進行比較: 使用==比較基本類型域, 使用equals比較對象域. 若是全部的域都匹配, 就返回true, 不然就返回flase:

① 若是在子類中從新定義equals()方法, 就要在其中包含調用super.equals(other);

② 當此方法被重寫時, 一般有必要重寫 hashCode() 方法, 以維護 hashCode 方法的常規協定, 該協定聲明 相等對象必須具備相等的哈希碼


參考資料

重寫equal()時爲何也得重寫hashCode()之深度解讀equal方法與hashCode方法淵源

版權聲明

做者: 馬瘦風

出處: 博客園 馬瘦風的博客

感謝閱讀, 若是文章有幫助或啓發到你, 點個[好文要頂👆] 或 [推薦👍] 吧😜

本文版權歸博主全部, 歡迎轉載, 但 [必須在文章頁面明顯位置給出原文連接], 不然博主保留追究相關人員法律責任的權利.

相關文章
相關標籤/搜索