K:java中的hashCode和equals方法

来源:http://www.cnblogs.com/MyStringIsNotNull/archive/2017/12/11/8023853.html
-Advertisement-
Play Games

  hashCode和equals方法是Object類的相關方法,而所有的類都是直接或間接的繼承於Object類而存在的,為此,所有的類中都存在著hashCode和equals。通過翻看Object類的相關源碼,發現其hashCode的實現方式如下:   從中 ...


  hashCode和equals方法是Object類的相關方法,而所有的類都是直接或間接的繼承於Object類而存在的,為此,所有的類中都存在著hashCode和equals。通過翻看Object類的相關源碼,發現其hashCode的實現方式如下:

public native int hashCode();

  從中可以看出,hashCode的實現是一個本地方法,並且其返回了一個int型的值。很多人都認為在預設情況下,hashCode返回的就是對象的存儲地址,事實上這樣的看法是不全面的,確實有些JVM在實現時是直接返回對象的存儲地址的,但是在大多數的時候,其只能說是與存儲地址有一定的關聯,下麵的是HotSpot JVM中生成hash散列值的實現:

@代碼來源:http://www.cnblogs.com/dolphin0520/p/3681042.html

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     // This form uses an unguarded global Park-Miller RNG,
     // so it's possible for two threads to race and generate the same RNG.
     // On MP system we'll have lots of RW access to a global, so the
     // mechanism induces lots of coherency traffic.
     value = os::random() ;
  } else
  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = intptr_t(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = intptr_t(obj) ;
  } else {
     // Marsaglia's xor-shift scheme with thread-specific state
     // This is probably the best overall implementation -- we'll
     // likely make this the default in future releases.
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = v ;
  }
 
  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}


ps:該實現位於hotspot/src/share/vm/runtime/synchronizer.cpp文件下。

  hashCode方法的主要作用是為了配合基於散列的集合一起正常運行,這樣的散列集合包括HashSet、HashMap以及HashTable。Hash相關的數據結構是根據對象的相關信息(可以稱為鍵),通過一定的運算規則將其散列映射到一個數值上的,為此,Hash相關的數據結構具有查找和插入的速度都較快的優點。Java中的hashCode方法就是根據一定的規則將與對象相關的信息(比如對象的存儲地址,對象的欄位等)映射成一個數值,這個數值稱作為散列值也稱為哈希值。而需要註意的一點是在一般情況下即使兩個對象的hash值相同也不能判定這兩個對象的相關信息是相同的,因為hash函數一般而言都不會是一個雙射函數,為此有碰撞產生的情況存在(即兩個不同的對象會得到相同的hash值)。為此,在比較兩個對象是否相同的時候,便需要有equals方法作為輔助了。

  至於equals方法,相信學過java基礎的人都會知道,在String等類中其用於判斷兩個對象的值是否相等,至於在Object類中,其實現如下:

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

  從中可以發現,對於Object類來說,其equals方法判斷的只是兩個對象是不是同一個對象而已,當為同一個對象的時候返回true,否則返回false。而這並不能夠實現判斷兩個對象的值是否相同的功能,通過查看String類中的equals方法的相關源碼:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
        char v1[] = value;
        char v2[] = anotherString.value;
        int i = offset;
        int j = anotherString.offset;
        while (n-- != 0) {
            if (v1[i++] != v2[j++])
            return false;
        }
        return true;
        }
    }
    return false;
    }

我們可以發現,其重寫了Object類的equals方法,使得其可以對兩個對象的值是否一致進行判斷。

  至此,我們對於hashCode和equals方法的作用有了一定的瞭解,hashCode方法的作用是根據對象的相關信息(對象的欄位值,對象的存儲地址)通過一定的運算規則獲得一個int型值,該int值在一定程度上反應了對象的有關信息。而equals方法的作用是用於判斷兩個對象的相關的指定信息是否是一樣的。在Hash相關的數據結構中通過結合使用hashCode方法以及equals方法來提高插入和查找效率。

例如,java.util.HashMap的中put方法的具體實現:

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
 
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

  一般而言,對於使用Hash相關的數據結構,其會先通過調用對象的hashCode方法獲取該對象的hash值,縮小了查找範圍之後,再通過調用equals方法進行“精細”的查找,以縮短查找時間和範圍,提高查找效率。

  通過以上的分析,我們需要註意到如下的幾點:

  1. 不同的對象可能會生成相同的hash值。為此,不能通過hash值來判斷兩個對象是否相等,但是可以通過hash值來判斷兩個對象的不相等,即hash值相等的兩個對象,其不一定是相等的,而hash值不相等的兩個對象,其一定是不相等的。
  2. equals方法返回結果為ture的一定是相等的對象

由以上的這兩點,我們可以推出如下的四點內容:

  1. 如果調用equals方法得到的結果為true,則兩個對象的hashcode值必定相等
  2. 如果equals方法得到的結果為false,則兩個對象的hashcode值不一定不同
  3. 如果兩個對象的hashcode值不等,則equals方法得到的結果必定為false
  4. 如果兩個對象的hashcode值相等,則equals方法得到的結果未知。

為此,我們可以知道,在重寫equals方法的時候,必須要重寫了hashCode方法。

在重寫equals方法和hashCode方法的時候,需要註意以下幾點:

  1. 在程式執行期間,只要equals方法的比較操作用到的信息沒有被修改,那麼對這同一個對象調用多次,hashCode方法必須始終如一地返回同一個整數。
  2. 如果兩個對象根據equals方法比較是相等的,那麼調用兩個對象的hashCode方法必須返回相同的整數結果。
  3. 如果兩個對象根據equals方法比較是不等的,則hashCode方法不一定得返回不同的整數。

------------------------------------------->摘自《Effective Java》

重寫equals和hashCode方法的一個例子:

public class HashCodeTest
{
    private String name="小小";
    private int age=12;
    @Override
    public int hashCode()
    {
        return age*37+name.hashCode();
    }
    @Override
    public boolean equals(Object other)
    {
        if(other==this)
            return true;
        if(other==null)
            return false;
        if(other instanceof HashCodeTest)
        {
            HashCodeTest t=(HashCodeTest) other;
            return this.age==t.age&&this.name.equals(t.name);
        }
        return false;
    }
}

註意的一點是:
在設計hashCode方法和equals方法的時候,如果對象中的數據易變,則最好在equals方法和hashCode方法中不要依賴於該欄位

回到目錄|·(工)·)


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

-Advertisement-
Play Games
更多相關文章
  •   在分析了hashCode方法和equals方法之後,我們對hashCode方法和equals方法的相關作用有了大致的瞭解。在通過查看HashMap類的相關源碼的時候,發現其中存在一個int hash(int h)的方法。在HashMap中該方法的源碼如下:  &em ...
  • 文章核心 其實,本不想把標題寫的那麼恐怖,只是發現很多人幹了幾年java以後,都自認為是一個不錯的java程式員了,可以拿著上萬的工資都處宣揚自己了,寫這篇文章的目的並不是嘲諷和我一樣做java的同行們,只是希望讀者看到此篇文章後,可以和我一樣,心平氣和的爭取做一個優秀的程式員。 講述方向 由於一直 ...
  • Properties類與.properties文件:   Properties類繼承自Hashtable類並且實現了Map介面,也是使用一種鍵值對的形式來保存屬性集的類,不過Properties有特殊的地方,就是它的鍵和值都是字元串類型。而.properties文件是由“鍵=值” ...
  • 使用過濾器改進應用程式 一、過濾器的目的 過濾器是可以攔截訪問資源的請求、資源的響應或者同時攔截兩者的應用組件。過濾器可以檢測和修改請求和響應,同時也可以拒絕、重定向或轉發請求。javax.servlet.Filter介面實現了過濾器技術,使用HttpServletRequest和HttpServl ...
  • String是最常使用的Java類之一,整理的了一些重要的String知識分享給大家。 作為一個Java新手程式員,對String進行更深入的瞭解很有必要。如果你是有幾年Java開發經驗,可以根據目錄選擇性的閱讀以下內容。 1、什麼是String,它是什麼數據類型? String是定義在 java. ...
  • 作為 Java 書呆子,比起實用技能,我們會對介紹 Java 和 JVM 的概念細節更感興趣。因此我想推薦 Lukas Eder 在 jooq.org 發表的原創作品給大家。 你是從很早開始就一直使用 Java 嗎?那你還記得它的過去嗎?那時,Java 還叫 Oak,OO 還是一個熱門話題,C++ ...
  • - 服務端軟體 - socket服務端 - 本地文件操作 - 解析指令【SQL語句】 - 客戶端軟體(各種各樣) - socket客戶端 - 發送指令 - 解析指令【SQL語句】 上述提到MySQL等軟體可以接受命令,並做出相應的操作,由於命令中可以包含刪除文件、獲取文件內容等眾多操作,對於編寫的命 ...
  • 相信每個小伙伴在使用laravel的時候都會記錄日誌、查看日誌。那麼問題來了,比如我在對接zabbix介面的時候,使用 Log::info() 會讓日誌全部記錄在 storage/logs/laravel.log 文件里,查找起來比較麻煩。那麼我可不可以單獨記錄在一個日誌文件里呢? laravel文 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...