1. 程式人生 > >基於jdk1.8的equals()與hashCode()方法詳解

基於jdk1.8的equals()與hashCode()方法詳解

閱讀目錄

  • equals()方法詳解
  • hashcode() 方法詳解
  • Hashset、Hashmap、Hashtable與hashcode()和equals()的密切關係

equals()方法詳解

equals()方法是用來判斷其他的物件是否和該物件相等.

equals()方法在object類中定義如下: 

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

很明顯是對兩個物件的地址值進行的比較(即比較引用是否相同)。但是我們知道,String 、Math、Integer、Double等這些封裝類在使用equals()方法時,已經

覆蓋了object類的equals()方法。

  比如在String類中如下:

public boolean equals(Object anObject) {  
        if (this == anObject) {  
            return true;  
        }  
        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;  
    }

很明顯,這是進行的內容比較,而已經不再是地址的比較。依次類推Math、Integer、Double等這些類都是重寫了equals()方法的,從而進行的是內容的比較。當然,基本型別是進行值的比較

它的性質有:

  • 自反性(reflexive)。對於任意不為null的引用值x,x.equals(x)一定是true

  • 對稱性(symmetric)。對於任意不為null的引用值xy,當且僅當x.equals(y)true時,y.equals(x)也是true

  • 傳遞性(transitive)。對於任意不為null的引用值xyz,如果x.equals(y)true,同時y.equals(z)

    true,那麼x.equals(z)一定是true

  • 一致性(consistent)。對於任意不為null的引用值xy,如果用於equals比較的物件資訊沒有被修改的話,多次呼叫時x.equals(y)要麼一致地返回true要麼一致地返回false

  • 對於任意不為null的引用值xx.equals(null)返回false


需要注意的是當equals()方法被override時,hashCode()也要被override。按照一般hashCode()方法的實現來說,相等的物件,它們的hash code一定相等。

hashcode() 方法詳解

hashCode()方法給物件返回一個hash code值。這個方法被用於hash tables,例如HashMap。

它的性質是:

  • 在一個Java應用的執行期間,如果一個物件提供給equals做比較的資訊沒有被修改的話,該物件多次呼叫hashCode()方法,該方法必須始終如一返回同一個integer。

  • 如果兩個物件根據equals(Object)方法是相等的,那麼呼叫二者各自的hashCode()方法必須產生同一個integer結果。

  • 不要求根據equals(java.lang.Object)方法不相等的兩個物件,呼叫二者各自的hashCode()方法必須產生不同的integer結果。然而,程式設計師應該意識到對於不同的物件產生不同的integer結果,有可能會提高hash table的效能。

大量的實踐表明,由Object類定義的hashCode()方法對於不同的物件返回不同的integer。

在object類中,hashCode定義如下:

public native int hashCode();
說明是一個本地方法,它的實現是根據本地機器相關的。當然我們可以在自己寫的類中覆蓋hashcode()方法,比如String、Integer、Double等這些類都是覆蓋了hashcode()方法的。例如在String類中定義的hashcode()方法如下:
public int hashCode() {  
        int h = hash;  
        if (h == 0 && value.length > 0) {  
            char val[] = value;  
  
            for (int i = 0; i < value.length; i++) {  
                h = 31 * h + val[i];  
            }  
            hash = h;  
        }  
        return h;  
    }

解釋一下這個程式(String的API中寫到):s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]
      使用 int 演算法,這裡 s[i] 是字串的第 i 個字元,n 是字串的長度,^ 表示求冪(空字串的雜湊碼為 0)。

       想要弄明白hashCode的作用,必須要先知道Java中的集合。  
       總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。這裡就引出一個問題:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?
        這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後新增到集合中的元素比較的次數就非常多了。也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要呼叫1000次equals方法。這顯然會大大降低效率。   
       於是,Java採用了雜湊表的原理。雜湊(Hash)實際上是個人名,由於他提出一雜湊演算法的概念,所以就以他的名字命名了。雜湊演算法也稱為雜湊演算法,是將資料依特定演算法直接指定到一個地址上,初學者可以簡單理解,hashCode方法實際上返回的就是物件儲存的實體地址(實際可能並不是)。  
       這樣一來,當集合要新增新的元素時,先呼叫這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就呼叫它的equals方法與新元素進行比較,相同的話就不存了,不相同就雜湊其它的地址。所以這裡存在一個衝突解決的問題。這樣一來實際呼叫equals方法的次數就大大降低了,幾乎只需要一兩次。  

 簡而言之,在集合查詢時,hashcode能大大降低物件比較次數,提高查詢效率!

Java物件的eqauls方法和hashCode方法是這樣規定的:

1、相等(相同)的物件必須具有相等的雜湊碼(或者雜湊碼)。

2、如果兩個物件的hashCode相同,它們並不一定相同。

 以下是Object物件API關於equal方法和hashCode方法的說明:

  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
  • 以上API說明是對之前2點的官方詳細說明

關於第一點,相等(相同)的物件必須具有相等的雜湊碼(或者雜湊碼),為什麼?

 想象一下,假如兩個Java物件A和B,A和B相等(eqauls結果為true),但A和B的雜湊碼不同,則A和B存入HashMap時的雜湊碼計算得到的HashMap內部陣列位置索引可能不同,那麼A和B很有可能允許同時存入HashMap,顯然相等/相同的元素是不允許同時存入HashMap,HashMap不允許存放重複元素。

 關於第二點,兩個物件的hashCode相同,它們並不一定相同

String a="Aa";
String b="BB";
int aa=a.hashCode();
int bb=b.hashCode();

 也就是說,不同物件的hashCode可能相同;假如兩個Java物件A和B,A和B不相等(eqauls結果為false),但A和B的雜湊碼相等,將A和B都存入HashMap時會發生雜湊衝突,也就是A和B存放在HashMap內部陣列的位置索引相同這時HashMap會在該位置建立一個連結表,將A和B串起來放在該位置,顯然,該情況不違反HashMap的使用原則,是允許的。當然,雜湊衝突越少越好,儘量採用好的雜湊演算法以避免雜湊衝突。

Hashset、Hashmap、Hashtable與hashcode()和equals()的密切關係

Hashset是繼承Set介面,Set介面又實現Collection介面,這是層次關係。那麼Hashset、Hashmap、Hashtable中的儲存操作是根據什麼原理來存取物件的呢?

        下面以HashSet為例進行分析,我們都知道:在hashset中不允許出現重複物件,元素的位置也是不確定的。在hashset中又是怎樣判定元素是否重複的呢?在java的集合中,判斷兩個物件是否相等的規則是:
         1.判斷兩個物件的hashCode是否相等

             如果不相等,認為兩個物件也不相等,完畢
             如果相等,轉入2
           (這一點只是為了提高儲存效率而要求的,其實理論上沒有也可以,但如果沒有,實際使用時效率會大大降低,所以我們這裡將其做為必需的。)

         2.判斷兩個物件用equals運算是否相等
            如果不相等,認為兩個物件也不相等
            如果相等,認為兩個物件相等(equals()是判斷兩個物件是否相等的關鍵)
            為什麼是兩條準則,難道用第一條不行嗎?不行,因為前面已經說了,hashcode()相等時,equals()方法也可能不等,所以必須用第2條準則進行限制,才能保證加入的為非重複元素。

class Student {
    int num;
    String name;

    Student(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int hashCode() {
        return num * name.hashCode();
    }

    public boolean equals(Object o) {
        Student s = (Student) o;
        return num == s.num && name.equals(s.name);
    }

    public String toString() {
        return num + ":" + name;
    }
}
1:zhangsan  
3:wangwu  
2:lisi  

可以看到重複元素的問題已經消除,根據重寫的方法,即便兩次呼叫了new Student(1,"zhangsan"),我們在獲得物件的雜湊碼時,根據重寫的方法hashcode(),獲得的雜湊碼肯定是一樣的,當然根據equals()方法我們也可判斷是相同的,所以在向hashset集合中新增時把它們當作重複元素看待了。

重寫equals()和hashcode()小結:

  1.重點是equals,重寫hashCode只是技術要求(為了提高效率)
      2.為什麼要重寫equals呢?因為在java的集合框架中,是通過equals來判斷兩個物件是否相等的
      3.在hibernate中,經常使用set集合來儲存相關物件,而set集合是不允許重複的。在向HashSet集合中新增元素時,其實只要重寫equals()這一條也可以。但當hashset中元素比較多時,或者是重寫的equals()方法比較複雜時,我們只用equals()方法進行比較判斷,效率也會非常低,所以引入了hashCode()這個方法,只是為了提高效率,且這是非常有必要的。如果這樣寫:

public int hashCode(){  
   return 1; //等價於hashcode無效  
}  

這樣做的效果就是在比較雜湊碼的時候不能進行判斷,因為每個物件返回的雜湊碼都是1,每次都必須要經過比較equals()方法後才能進行判斷是否重複,這當然會引起效率的大大降低。







相關推薦

基於jdk1.8的equals()hashCode()方法

閱讀目錄 equals()方法詳解hashcode() 方法詳解 Hashset、Hashmap、Hashtable與hashcode()和equals()的密切關係 equals()方法詳解 equals()方法是用來判斷其他的物件是否和該物件相等. equals()方法在object類中定義如

“全棧2019”Java第五十七章:多型構造方法

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第五十七章:多型與構造方法詳解 下一章 “全棧2019”Java第五十八章:多型中方法返回型

“全棧2019”Java第五十九章:抽象類抽象方法

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第五十九章:抽象類與抽象方法詳解 下一章 “全棧2019”Java第六十章:如何定義介面

“全棧2019”Java第六十三章:介面抽象方法

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第六十三章:介面與抽象方法詳解 下一章 “全棧2019”Java第六十四章:介面與靜態方法詳

“全棧2019”Java第六十四章:接口靜態方法

分享圖片 java 回復 開發語言 java學習 dea 接口 了解 學習計劃 難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文鏈接 “全棧2019”Java第六十四章:

String 的hashCode 方法

先貼上String 類的hashCode 方法 public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val

Java中的equals和hashCode方法

Java中的equals方法和hashCode方法是Object中的,所以每個物件都是有這兩個方法的,有時候我們需要實現特定需求,可能要重寫這兩個方法,今天就來介紹一些這兩個方法的作用。 equa

jQuery事件繫結on()、bind()delegate() 方法

使用JS一段時間了,專案過程中發現在jQuery中繫結事件時,有人用bind(),有人用on(),有人用delegate(),還有人用live(),看程式碼的時候覺得都實現功能了也就掀過去了,只是一直沒完全弄懂之間的區別,於是今天查了下資料,自己做個總結。 之

繫結事件on()、bind()delegate() 方法

啃了一段日子的js相關了,學的過程中發現在jQuery中繫結事件時,有人用bind(),有人用on(),有人用delegate(),還有人用live(),看程式碼

Java8中Optional類定義使用方法

概述 到目前為止,著名的NullPointerException是導致Java應用程式失敗的最常見原因。過去,為了解決空指標異常,Google公司著名的Guava專案引入了Optional類,Guava通過使用檢查空值的方式來防止程式碼汙染,它鼓勵程式設計師寫更乾淨的程式碼。受到Goo

基於JDK1.7的HashMap原始碼

如有不對的地方,請指出,謝謝! 一、HashMap概述 HashMap是基於雜湊表的Map介面實現,此實現提供所有可選的對映操作,並允許使用null值和null鍵。HashMap與HashTable的作用大致相同,但是它不是執行緒安全的。此類不保證對映的

Lua元表方法(轉)

Lua中提供的元表是用於幫助Lua資料變數完成某些非預定義功能的個性化行為,如兩個table的相加。假設a和b都是table,通過元表可以定義如何計算表示式a+b。當Lua試圖將兩個table相加時,它會先檢查兩者之一是否有元表,然後檢查該元表中是否存在_

PHP介面繼承及介面多繼承原理實現方法

在PHP的介面中,介面可以繼承介面。雖然PHP類只能繼承一個父類(單繼承),但是介面和類不同,介面可以實現多繼承,可以繼承一個或者多個介面。當然介面的繼承也是使用extends關鍵字,要多個繼承的話只要用逗號把繼承的介面隔開即可。 需要注意的是當你介面繼承其它介面時候,

常見Java記憶體溢位解決方法

Java programming language具有目前大部分程式語言所共有的一些特徵,被特意設計用於網際網路的分散式環境。Java具有類似於C++語言的"形式和感覺",但它要比C++語言更易於使用,而且在程式設計時徹底採用了一種"以物件為導向"的方式。使用Java編寫的

Win7下SQLite安裝配置使用方法

前言 SQLite 是一個軟體庫,實現了自給自足的、無伺服器的、零配置的、事務性的 SQL 資料庫引擎。SQLite 是在世界上最廣泛部署的 SQL 資料庫引擎。SQLite 原始碼不受版權限制。 簡單的認識了SQLite之後,我就很想來嘗試一下,他如此的輕量,作為一個程式設計師,我沒有理由不去學習一

Date beforeafter方法

Date1.after(Date2),當Date1大於Date2時,返回TRUE,當小於等於時,返回false;即Date2比Date1小的true/false,當Date2日期比Date1小的時候為t

[js高手之路]原型對象(prototype)原型鏈相關屬性方法

隱式 之前 username tar uname create pro getproto .get 一,instanceof: instanceof檢測左側的__proto__原型鏈上,是否存在右側的prototype原型. 我在之前的兩篇文章 [js高手之路]構造函數的基

真實感海洋的繪制(一):基於統計學模型的水面模擬方法

最簡 自動生成 nbsp imu gif bubuko fourier div img 真實感海洋的繪制(一):基於統計學模型的水面模擬方法詳解 學習了基本的OpenGL和圖形學知識後,第一個想做的事情就是畫水(笑),因為對我而言各種遊戲裏面往往最令人印象深刻的就是那波光粼

面向物件—的__new__()方法 [Python] Python 之 __new__() 方法例項化

[Python] Python 之 __new__() 方法與例項化   __new__() 是在新式類中新出現的方法,它作用在構造方法建造例項之前,可以這麼理解,在 Python 中存在於類裡面的構造方法 __init__() 負責將類的例項化,而在 __init__()

HashCode()和equals()方法

這兩個方法的問題一直困擾了我很久。不清楚他們各自的用處和實現,記得在實習的時候遇到過一個問題:將自定義的物件放在一個set集合中,目的是篩選出不重複的物件集合,但是結果卻是錯誤的。比如說有10個物件,但是其中有兩個是相等的,那麼期望的set的大小應該是9,但是最終結果卻是10。讓我很奇怪,後來帶我的