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

来源:https://www.cnblogs.com/shoufeng/archive/2019/05/01/10800396.html
-Advertisement-
Play Games

本篇文章有如下方面: ① equals()與‘==’的區別; ② equals()方法的重寫規則(5條); ③ 為什麼重寫equals()的同時還需要重寫hashCode(); ④ JDK 7中對hashCode()方法的改進; ⑤ Java API文檔中關於hashCode()方法的規定; ⑥ 重... ...


目錄

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

預設情況下也就是從超類Object繼承而來的equals()方法與‘==’是完全等價的, 比較的都是對象的記憶體地址.

但我們可以重寫equals()方法, 使其按照我們的需求的方式進行比較, 比如String類就重寫了equals方法, 它比較的是字元的序列, 而不再是記憶體地址.

2 equals()方法的重寫規則

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

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

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

(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.

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

3 為什麼重寫equals()的同時還需要重寫hashCode()

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

在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方法淵源

版權聲明

作者: 馬瘦風

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

感謝閱讀, 如果文章有幫助或啟發到你, 點個[

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Python基礎之參數與返回值進階,包括 函數的返回值 進階,函數的參數進階,函數的遞歸。其中,函數的返回值 進階 包括 利用元組返回多個函數值,用多個變數接收函數的返回值;函數的參數進階 包括 函數內部變數和參數的關係,列表調用+=,預設參數,多值參數,元組和字典的拆包;函數的遞歸 僅包括 函數的... ...
  • 概述 這是關於 Swoole 學習的第三篇文章:Swoole WebSocket 的應用。 "第二篇:Swoole Task 的應用" "第一篇:Swoole Timer 的應用" 什麼是 WebSocket ? WebSocket 是一種在單個TCP連接上進行全雙工通信的協議。 WebSocket ...
  • 工作一年,維護工程項目的同時一直寫CURD,最近學習DDD,結合之前自己寫的開源項目,深思我們這種CURD的編程方式的弊端,和朋友討論後,發現我們從來沒有面向對象開發,所以寫這篇文章,希望更多人去思考面向對象,不只是停留在背書上 下麵以開發一個常規的登錄模塊為例,模擬實現一個登錄功能,一步步地去說明 ...
  • 5.1自學自我總結 1.關於數據類型補充 在整列中除了int數據類型,還要long數據數據類型 ling為長數字 另外還有一種未被提到的數據類型為虛數complex在python中虛數為5j來表示 個個數字函數的也可以相互裝換 如下 2.關於字元串 字元串取值 3.不同類型變數拼接新增方法 五一快樂 ...
  • 關於Java 鎖的知識整理與回顧(個人筆記): 鎖有哪些,分別用來幹嘛? Java實現鎖有兩種方式,synchronized關鍵字和Lock (1)Lock(可判斷鎖狀態) Lock是基於JDK層面實現。Lock的實現主要有ReentrantLock、ReadLock和WriteLock(引出鎖分類 ...
  • 利用map和reduce編寫一個str2float函數,把字元串'123.456'轉換成浮點數123.456。 思路:計算小數位數 >將字元串中的小數點去掉 >字元串轉換為整數 >整數轉換為浮點數 知識點: 1、將字元串中的小數點去掉可以用切片的方法。 2、reduce把一個函數作用在一個序列[x1 ...
  • 最近在web項目中,客戶端註冊時需要通過郵箱驗證,伺服器就需要向客戶端發送郵件,我把發送郵件的細節進行了簡易的封裝: 最近在web項目中,客戶端註冊時需要通過郵箱驗證,伺服器就需要向客戶端發送郵件,我把發送郵件的細節進行了簡易的封裝: 在maven中需要導入: 1 <!--Email--> 2 <d ...
  • 7.1 包 7.1.1 看一個應用場景 現在有兩個程式員共同開發一個項目,程式員xiaoming希望定義一個類取名Dog,程式員xiaohong也想定一個類也叫Dog,兩個程式員還為此吵了起來,該怎麼辦? >使用包即可解決這個問題 7.1.2 回顧-Java包的三大作用 1) 區分相同名字的類 2) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...