最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

重寫hashCode()和equals()方法詳細介紹

 更新時間:2018年01月08日 15:33:24   作者:司青  
這篇文章主要介紹了重寫hashCode()和equals()方法詳細介紹,涉及重寫equals()方法,重寫hashCode()方法,重寫equals()而不重寫hashCode()的風險等相關內容的介紹,具有一定借鑒價值,需要的朋友可以參考下

hashCode()和equals()方法可以說是Java完全面向對象的一大特色.它為我們的編程提供便利的同時也帶來了很多危險.這篇文章我們就討論一下如何正解理解和使用這2個方法.

如何重寫equals()方法

如果你決定要重寫equals()方法,那么你一定要明確這么做所帶來的風險,并確保自己能寫出一個健壯的equals()方法.一定要注意的一點是,在重寫equals()后,一定要重寫hashCode()方法.具體原因稍候再進行說明.

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

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

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

1. 自反性:A.equals(A)要返回true.
2. 對稱性:如果A.equals(B)返回true, 則B.equals(A)也要返回true.
3. 傳遞性:如果A.equals(B)為true, B.equals(C)為true, 則A.equals(C)也要為true. 說白了就是 A = B , B = C , 那么A = C.
4. 一致性:只要A,B對象的狀態(tài)沒有改變,A.equals(B)必須始終返回true.
5. A.equals(null) 要返回false.

相信只要不是專業(yè)研究數(shù)學的人,都對上面的東西不來電.在實際應用中我們只需要按照一定的步驟重寫equals()方法就可以了.為了說明方便,我們先定義一個程序員類(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類中你自定義的數(shù)據(jù)域,name和age,一個都不能少.

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

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

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

如何重寫hashCode()方法

在JavaSE 7 Specification中指出,

"Note that it is generally necessary to override the hashCode method whenever this method(equals) is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes."

如果你重寫了equals()方法,那么一定要記得重寫hashCode()方法.我們在大學計算機數(shù)據(jù)結構課程中都已經(jīng)學過哈希表(hash table)了,hashCode()方法就是為哈希表服務的.

當我們在使用形如HashMap, HashSet這樣前面以Hash開頭的集合類時,hashCode()就會被隱式調用以來創(chuàng)建哈希映射關系.稍后我們再對此進行說明.這里我們先重點關注一下hashCode()方法的寫法.

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

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

對于本例,我們可以這么寫:

@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()方法是如何實現(xiàn)的.查文檔知:

"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的實現(xiàn)是很嚴謹?shù)? 這樣能最大程度避免2個不同的String會出現(xiàn)相同的hashCode的情況.

重寫equals()而不重寫hashCode()的風險

在Oracle的Hash Table實現(xiàn)中引用了bucket的概念.如下圖所示:

從上圖中可以看出,帶bucket的hash table大致相當于哈希表與鏈表的結合體.即在每個bucket上會掛一個鏈表,鏈表的每個結點都用來存放對象.Java通過hashCode()方法來確定某個對象應該位于哪個bucket中,然后在相應的鏈表中進行查找.在理想情況下,如果你的hashCode()方法寫的足夠健壯,那么每個bucket將會只有一個結點,這樣就實現(xiàn)了查找操作的常量級的時間復雜度.即無論你的對象放在哪片內存中,我都可以通過hashCode()立刻定位到該區(qū)域,而不需要從頭到尾進行遍歷查找.這也是哈希表的最主要的應用.

如:

當我們調用HashSet的put(Object o)方法時,首先會根據(jù)o.hashCode()的返回值定位到相應的bucket中,如果該bucket中沒有結點,則將 o 放到這里,如果已經(jīng)有結點了, 則把 o 掛到鏈表末端.同理,當調用contains(Object o)時,Java會通過hashCode()的返回值定位到相應的bucket中,然后再在對應的鏈表中的結點依次調用equals()方法來判斷結點中的對象是否是你想要的對象.

下面我們通過一個例子來體會一下這個過程:

我們先創(chuàng)建2個新的Coder對象:

Coder c1 = new Coder("bruce", 10); 
 Coder c2 = new Coder("bruce", 10); 

假定我們已經(jīng)重寫了Coder的equals()方法而沒有重寫hashCode()方法:

@Override 
 public boolean equals(Object other) { 
 System.out.println("equals method invoked!"); 
  
 if(other == this) 
  return true; 
 if(!(other instanceof Coder)) 
  return false; 
  
 Coder o = (Coder)other; 
 return o.name.equals(name) && o.age == age; 
 } 

然后我們構造一個HashSet,將c1對象放入到set中:

Set<Coder> set = new HashSet<Coder>(); 
 set.add(c1); 

再執(zhí)行:

System.out.println(set.contains(c2)); 

我們期望contains(c2)方法返回true, 但實際上它返回了false.

c1和c2的name和age都是相同的,為什么我把c1放到HashSet中后,再調用contains(c2)卻返回false呢?這就是hashCode()在作怪了.因為你沒有重寫hashCode()方法,所以HashSet在查找c2時,會在不同的bucket中查找.比如c1放到05這個bucket中了,在查找c2時卻在06這個bucket中找,這樣當然找不到了.因此,我們重寫hashCode()的目的在于,在A.equals(B)返回true的情況下,A, B 的hashCode()要返回相同的值.

我讓hashCode()每次都返回一個固定的數(shù)行嗎

有人可能會這樣重寫:

@Override 
 public int hashCode() { 
 return 10; 
 
 } 

如果這樣的話,HashMap, HashSet等集合類就失去了其 "哈希的意義".用<Effective Java>中的話來說就是,哈希表退化成了鏈表.如果hashCode()每次都返回相同的數(shù),那么所有的對象都會被放到同一個bucket中,每次執(zhí)行查找操作都會遍歷鏈表,這樣就完全失去了哈希的作用.所以我們最好還是提供一個健壯的hashCode()為妙.

總結

以上就是本文關于重寫hashCode()和equals()方法詳細介紹的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

相關文章

  • @PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter注解的用法詳解

    @PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter注解的用法詳解

    這篇文章主要介紹了@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter注解的用法詳解,通過在方法上添加@PreAuthorize注解,可以指定需要滿足的權限條件,只有滿足條件的用戶才能執(zhí)行該方法,需要的朋友可以參考下
    2023-10-10
  • 基于Java和XxlCrawler獲取各城市月度天氣情況實踐分享

    基于Java和XxlCrawler獲取各城市月度天氣情況實踐分享

    本文主要講解使用Java開發(fā)語言,使用XxlCrawler框架進行智能的某城市月度天氣抓取實踐開發(fā),文章首先介紹目標網(wǎng)站的相關頁面及目標數(shù)據(jù)的元素,然后講解在信息獲取過程的一些參數(shù)配置以及問題應對,需要的朋友可以參考下
    2024-05-05
  • Springboot整合支付寶支付功能

    Springboot整合支付寶支付功能

    這篇文章主要介紹了Springboot整合支付寶支付功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-01-01
  • 學習Java的Date、Calendar日期操作

    學習Java的Date、Calendar日期操作

    Java開發(fā)過程中避免不了日期相關操作,這篇文章總結了一些Date、Calendar的常用方法,需要的朋友可以參考下
    2015-07-07
  • Spring MVC-@RequestMapping注解詳解

    Spring MVC-@RequestMapping注解詳解

    @RequestMapping注解的作用,就是將請求和處理請求的控制器方法關聯(lián)起來,建立映射關系。這篇文章主要給大家介紹了關于SpringMVC中@RequestMapping注解用法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-04-04
  • Java NIO三大組件與ByteBuffer深入理解及使用

    Java NIO三大組件與ByteBuffer深入理解及使用

    這篇文章主要介紹了Java NIO三大組件與ByteBuffer,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2023-01-01
  • 詳解Flutter TabLayout 布局用法

    詳解Flutter TabLayout 布局用法

    Flutter是谷歌的移動UI框架,可以快速在iOS和Android上構建高質量的原生用戶界面。這篇文章主要介紹了Flutter TabLayout 布局用法,需要的朋友可以參考下
    2019-07-07
  • Java常用數(shù)字工具類 數(shù)字轉漢字(1)

    Java常用數(shù)字工具類 數(shù)字轉漢字(1)

    這篇文章主要為大家詳細介紹了Java常用數(shù)字工具類,數(shù)字轉漢字,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • SpringBoot + 微信公眾號JSAPI支付功能的實現(xiàn)

    SpringBoot + 微信公眾號JSAPI支付功能的實現(xiàn)

    這篇文章主要介紹了SpringBoot + 微信公眾號JSAPI支付功能的實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • spring如何解決循環(huán)依賴問題

    spring如何解決循環(huán)依賴問題

    Spring在單例模式下用三級緩存設計解決setter方法注入bean屬性循環(huán)依賴問題,但無法解決多例Bean和構造方法注入?yún)?shù)的循環(huán)依賴,三級緩存通過A、B兩對象互相注入屬性的過程解決循環(huán)依賴,其中,構造方法的循環(huán)依賴無法解決是因為創(chuàng)建對象會走構造方法
    2024-10-10

最新評論

上饶市| 区。| 上栗县| 浪卡子县| 鹤壁市| 青州市| 池州市| 汉沽区| 辽阳县| 济宁市| 曲麻莱县| 陇川县| 常德市| 兴隆县| 大丰市| 武宁县| 三明市| 林口县| 金坛市| 满洲里市| 镇赉县| 府谷县| 襄垣县| 综艺| 道真| 那曲县| 马关县| 尼木县| 辽中县| 保定市| 盐亭县| 张家口市| 华坪县| 名山县| 宿迁市| 镇宁| 塔城市| 连江县| 常宁市| 甘谷县| 阳信县|