日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

为什么要重写hashCode()方法和equals()方法以及如何进行重写

發布時間:2025/3/12 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为什么要重写hashCode()方法和equals()方法以及如何进行重写 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

本篇文章主要探討的問題有三個:

1、首先我們為什么需要重寫hashCode()方法和equals()方法?
2、在什么情況下需要重寫hashCode()方法和equals()方法?
3、如何重寫這兩個方法?

二、為什么需要重寫hashCode()方法和equals()方法

首先,為什么要重寫equals()方法。我們在定義類時,我們經常會希望兩個不同對象的某些屬性值相同時就認為他們相同,所以我們要重寫equals()方法,按照原則,我們重寫了equals()方法,也要重寫hashCode()方法。

Java中的超類Object類中定義的equals()方法是用來比較兩個引用所指向的對象的內存地址是否一致,Object類中equals()方法的源碼:

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

Object類中hashCode()方法的源碼:

public native int hashCode();

Object類中的hashCode()方法,用的是native關鍵字修飾,說明這個方法是個原生函數,也就說這個方法的實現不是用java語言實現的,是使用c/c++實現的,并且被編譯成了DLL,由java去調用,jdk源碼中不包含。對于不同的平臺它們是不同的,java在不同的操作系統中調用不同的native方法實現對操作系統的訪問,因為java語言不能直接訪問操作系統底層,因為它沒有指針。

(1)這種方法調用的過程:

1、在java中申明native方法,然后編譯;
2、用javah產生一個 .h 文件;
3、寫一個 .cpp文件實現native導出方法,其中需要包含第二步產生的.h文件(其中又包含了jdk帶的jni.h文件);
4、將.cpp文件編譯成動態鏈接庫文件;
5、在java中用System.loadLibrary()文件加載第四步產生的動態鏈接庫文件,然后這個navite方法就可被訪問了
Java的API文檔對hashCode()方法做了詳細的說明,這也是我們重寫hashCode()方法時的原則【Object類】;

(2)重點要注意的是:

a. 在java應用程序運行時,無論何時多次調用同一個對象時的hashCode()方法,這個對象的hashCode()方法的返回值必須是相同的一個int值;

b. 如果兩個對象equals()返回值為true,則他們的hashCode()有返回相同的int值;

c. 如果兩個對象equals()返回值為false,則他們的hashCode()值也有可能相同;

現在,我們到這里可以看出,我們重寫了equals()方法也要重寫hashCode()方法,這是因為要保證上面所述的b,c原則;所以java中的很多類都重寫了這兩個方法,例如String類,包裝類等。

三、在什么情況下需要重寫hashCode()方法和equals()方法

當我們自定義的一個類,想要把它的實例保存在集合中時,我們就需要重寫這兩個方法;集合(Collection)有兩個類,一個是List,一個是Set。

List:集合中的元素是有序的,可以重復的; Set:無序,不可重復的;12

(1)以HashSet來舉例:

HashSet存放元素時,根據元素的hashCode方法計算出該對象的哈希碼,快速找到要存儲的位置,然后進行比較,

比較過程如下:

  • 如果該對象哈希碼與集合已存在對象的哈希碼不一致,則該對象沒有與其他對象重復,添加到集合中!
  • 如果存在于該對象相同的哈希碼,那么通過equals方法判斷兩個哈希碼相同的對象是否為同一對象(判斷的標準是:屬性是否相同)
    • 相同對象,不添加。
    • 不同對象,添加。

注意:如果返回值為false,則這個時候會以鏈表的形式在同一個位置上存放兩個元素,這會使得HashSet的性能降低,因為不能快速定位了。示意圖如下:

還有一種情況就是兩個對象的hashCode()返回值不同,但是equals()返回true,這個時候HashSet會把這兩個對象都存進去,這就和Set集合不重復的規則相悖了;所以,我們重寫了equals()方法時,要按照b,c規則重寫hashCode()方法!

四、如何重寫這兩個方法

如果你決定要重寫equals()方法,那么你一定還要明確這么做所帶來的風險,并確保自己能寫出一個健壯的equals()方法。

一定要注意的一點是,在重寫equals()后,一定要重寫hashCode()方法。

(1)我們先看看 JavaSE 8 Specification中對equals()方法的說明:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).

這段話用了很多離散數學中的術數.簡單說明一下:

  • 自反性:A.equals(A)要返回true;
  • 對稱性:如果A.equals(B)返回true, 則B.equals(A)也要返回true;
  • 傳遞性:如果A.equals(B)為true, B.equals?為true, 則A.equals?也要為true. 說白了就是 A = B , B = C , 那么A = C;
  • 一致性:只要A,B對象的狀態沒有改變,A.equals(B)必須始終返回true;
  • A.equals(null) 要返回false;
  • (2)簡單步驟:

    為了說明方便,我們先定義一個程序員類(Coder):

    class Coder { private String name; private int age; // getters and setters }

    我們想要的是,如果2個程序員對象的name和age都是相同的,那么我們就認為這兩個程序員是一個人。這時候我們就要重寫其equals()方法。因為默認的equals()實際是判斷兩個引用是否指向內在中的同一個對象,相當于 == 。 重寫時要遵循以下三步:

    1、判斷是否等于自身:

    if(other == this){return true; }

    2、 使用instanceof運算符判斷 other 是否為Coder類型的對象:

    if(!(other instanceof Coder)) {return false; }

    3、比較Coder類中你自定義的數據域,name和age,一個都不能少:

    Coder o = (Coder)other; return o.name.equals(name) && o.age == age;

    看到這有人可能會問,第3步中有一個強制轉換,如果有人將一個Integer類的對象傳到了這個equals中,那么會不會扔ClassCastException呢?這個擔心其實是多余的.因為我們在第二步中已經進行了instanceof 的判斷,如果other是非Coder對象,甚至other是個null, 那么在這一步中都會直接返回false, 從而后面的代碼得不到執行的機會。

    上面的三步也是<Effective Java>中推薦的步驟,基本可保證萬無一失。

    我們在大學計算機數據結構課程中都已經學過哈希表(hash table)了,hashCode()方法就是為哈希表服務的。

    當我們在使用形如HashMap, HashSet這樣前面以Hash開頭的集合類時,hashCode()就會被隱式調用以來創建哈希映射關系。

    <Effective Java>中給出了一個能最大程度上避免哈希沖突的寫法,但我個人認為對于一般的應用來說沒有必要搞的這么麻煩.如果你的應用中HashSet中需要存放上萬上百萬個對象時,那你應該嚴格遵循書中給定的方法.如果是寫一個中小型的應用,那么下面的原則就已經足夠使用了:

    要保證Coder對象中所有的成員都能在hashCode中得到體現.

    @Override public int hashCode() { int result = 17; result = result * 31 + name.hashCode(); result = result * 31 + age; return result; }

    其中int result = 17你也可以改成20, 50等等都可以.看到這里我突然有些好奇,想看一下String類中的hashCode()方法是如何實現的.查文檔知:

    “Returns a hash code for this string. The hash code for a String object is computed as

    s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

    using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.)”

    對每個字符的ASCII碼計算n - 1次方然后再進行加和,可見Sun對hashCode的實現是很嚴謹的. 這樣能最大程度避免2個不同的String會出現相同的hashCode的情況.


    參考文章:

    1、http://blog.csdn.net/jing_bufferfly/article/details/50868266
    2、http://blog.csdn.net/neosmith/article/details/17068365

    總結

    以上是生活随笔為你收集整理的为什么要重写hashCode()方法和equals()方法以及如何进行重写的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。