1. 程式人生 > >hashcode(),equal()方法深入解析

hashcode(),equal()方法深入解析

首先,想要明白hashCode的作用,必須要先知道Java中的集合。  

總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。 前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。

那麼這裡就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢? 這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後新增到集合中的元素比較的次數就非常多了。 也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要呼叫1000次equals方法。這顯然會大大降低效率。  

於是,Java採用了雜湊表的原理。雜湊(Hash)實際上是個人名,由於他提出一雜湊演算法的概念,所以就以他的名字命名了。 雜湊演算法也稱為雜湊演算法,是將資料依特定演算法直接指定到一個地址上。初學者可以這樣理解,hashCode方法實際上返回的就是物件儲存的實體地址(實際可能並不是)。  

這樣一來,當集合要新增新的元素時,先呼叫這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。 如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了, 就呼叫它的equals方法與新元素進行比較,相同的話就不存了,不相同就雜湊其它的地址。 所以這裡存在一個衝突解決的問題。這樣一來實際呼叫equals方法的次數就大大降低了,幾乎只需要一兩次。 

所以,Java對於eqauls方法和hashCode方法是這樣規定的:

1、如果兩個物件相同,那麼它們的hashCode值一定要相同;

2、如果兩個物件的hashCode相同,它們並不一定相同(上面說的物件相同指的是用eqauls方法比較。)  

你當然可以不按要求去做了,但你會發現,相同的物件可以出現在Set集合中。同時,增加新元素的效率會大大下降。

hashcode這個方法是用來鑑定2個物件是否相等的。 那你會說,不是還有equals這個方法嗎? 不錯,這2個方法都是用來判斷2個物件是否相等的。但是他們是有區別的。 一般來講,equals這個方法是給使用者呼叫的,如果你想判斷2個物件是否相等,你可以重寫equals方法,然後在程式碼中呼叫,就可以判斷他們是否相等 了。簡單來講,equals方法主要是用來判斷從表面上看或者從內容上看,2個物件是不是相等。

舉個例子,有個學生類,屬性只有姓名和性別,那麼我們可以 認為只要姓名和性別相等,那麼就說這2個物件是相等的。 hashcode方法一般使用者不會去呼叫,比如在hashmap中,由於key是不可以重複的,他在判斷key是不是重複的時候就判斷了hashcode 這個方法,而且也用到了equals方法。這裡不可以重複是說equals和hashcode只要有一個不等就可以了!所以簡單來講,hashcode相 當於是一個物件的編碼,就好像檔案中的md5,他和equals不同就在於他返回的是int型的,比較起來不直觀。我們一般在覆蓋equals的同時也要 覆蓋hashcode,讓他們的邏輯一致。舉個例子,還是剛剛的例子,如果姓名和性別相等就算2個物件相等的話,那麼hashcode的方法也要返回姓名 的hashcode值加上性別的hashcode值,這樣從邏輯上,他們就一致了。 要從物理上判斷2個物件是否相等,用==就可以了。

在Java語言中,equals()和hashCode()兩個函式的使用是緊密配合的,你要是自己設計其中一個,就要設計另外一個。在多數情況 下,這兩個函式是不用考慮的,直接使用它們的預設設計就可以了。但是在一些情況下,這兩個函式最好是自己設計,才能確保整個程式的正常執行。最常見的是當 一個物件被加入收集物件(collection object)時,這兩個函式必須自己設計。更細化的定義是:如果你想將一個物件A放入另一個收集物件B裡,或者使用這個物件A為查詢一個元物件在收集對 象B裡位置的鑰匙,並支援是否容納,刪除收集物件B裡的元物件這樣的操作,那麼,equals()和hashCode()函式必須開發者自己定義。其他情 況下,這兩個函式是不需要定義的。

equals():

它是用於進行兩個物件的比較的,是物件內容的比較,當然也能用於進行物件參閱值的比較。什麼是物件參閱值的比較?就是兩個參閱變數的值得比較,我們 都知道參閱變數的值其實就是一個數字,這個數字可以看成是鑑別不同物件的代號。兩個物件參閱值的比較,就是兩個數字的比較,兩個代號的比較。這種比較是默 認的物件比較方式,在Object這個物件中,這種方式就已經設計好了。所以你也不用自己來重寫,浪費不必要的時間。

物件內容的比較才是設計equals()的真正目的,Java語言對equals()的要求如下,這些要求是必須遵循的。否則,你就不該浪費時間:

•對稱性:如果x.equals(y)返回是“true”,那麼y.equals(x)也應該返回是“true”。

•反射性:x.equals(x)必須返回是“true”。

•類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那麼z.equals(x)也應該返回是“true”。

•還有一致性:如果x.equals(y)返回是“true”,只要x和y內容一直不變,不管你重複x.equals(y)多少次,返回都是“true”。

•任何情況下,x.equals(null),永遠返回是“false”;x.equals(和x不同型別的物件)永遠返回是“false”。

hashCode():
這個函式返回的就是一個用來進行赫希操作的整型代號,請不要把這個代號和前面所說的參閱變數所代表的代號弄混了。後者不僅僅是個代號還具有在記憶體中才查詢對 象的位置的功能。hashCode()所返回的值是用來分類物件在一些特定的收集物件中的位置。這些物件是HashMap, Hashtable, HashSet,等等。這個函式和上面的equals()函式必須自己設計,用來協助HashMap, Hashtable, HashSet,等等對自己所收集的大量物件進行搜尋和定位。

這些收集物件究竟如何工作的,想象每個元物件hashCode是一個箱子的 編碼,按照編碼,每個元物件就是根據hashCode()提供的代號歸入相應的箱子裡。所有的箱子加起來就是一個HashSet,HashMap,或 Hashtable物件,我們需要尋找一個元物件時,先看它的程式碼,就是hashCode()返回的整型值,這樣我們找到它所在的箱子,然後在箱子裡,每 個元物件都拿出來一個個和我們要找的物件進行對比,如果兩個物件的內容相等,我們的搜尋也就結束。這種操作需要兩個重要的資訊,一是物件的 hashCode(),還有一個是物件內容對比的結果。

hashCode()的返回值和equals()的關係如下:

•如果x.equals(y)返回“true”,那麼x和y的hashCode()必須相等。

•如果x.equals(y)返回“false”,那麼x和y的hashCode()有可能相等,也有可能不等。

為什麼這兩個規則是這樣的,原因其實很簡單,拿HashSet來說吧,HashSet可以擁有一個或更多的箱子,在同一個箱子中可以有一個 或更多的獨特元物件(HashSet所容納的必須是獨特的元物件)。這個例子說明一個元物件可以和其他不同的元物件擁有相同的hashCode。但是一個 元物件只能和擁有同樣內容的元物件相等。所以這兩個規則必須成立。

設計這兩個函式所要注意到的:
如果你設計的物件型別並不使用於收集性物件,那麼沒有必要自己再設計這兩個函式的處理方式。這是正確的面向物件設計方法,任何使用者一時用不到的功能,就先不要設計,以免給日後功能擴充套件帶來麻煩。

如果你在設計時想別出心裁,不遵守以上的兩套規則,那麼勸你還是不要做這樣想入非非的事。我還沒有遇到過哪一個開發者和我說設計這兩個函式要違背前面說的兩個規則,我碰到這些違反規則的情況時,都是作為設計錯誤處理。

當一個物件型別作為收集型物件的元物件時,這個物件應該擁有自己處理equals(),和/或處理hashCode()的設計,而且要遵守前面所說 的兩種原則。equals()先要查null和是否是同一型別。查同一型別是為了避免出現ClassCastException這樣的異常給丟出來。查 null是為了避免出現NullPointerException這樣的異常給丟出來。

如果你的物件裡面容納的資料過多,那麼這兩個函式 equals()和hashCode()將會變得效率低。如果物件中擁有無法serialized的資料,equals()有可能在操作中出現錯誤。想象 一個物件x,它的一個整型資料是transient型(不能被serialize成二進位制資料流)。然而equals()和hashCode()都有依靠 這個整型資料,那麼,這個物件在serialization之前和之後,是否一樣?答案是不一樣。因為serialization之前的整型資料是有效的 資料,在serialization之後,這個整型資料的值並沒有儲存下來,再重新由二進位制資料流轉換成物件後,兩者(物件在serialization 之前和之後)的狀態已經不同了。這也是要注意的。

知道以上這些能夠幫助你:

1. 進行更好的設計和開發。

2. 進行更好的測試案例開發。

3. 在面試過程中讓面試者對你的學識淵博感到滿意。

相關推薦

hashcode(),equal()方法深入解析

首先,想要明白hashCode的作用,必須要先知道Java中的集合。   總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。 前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。 那麼這裡就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元

Java equals 方法hashcode 方法深入解析

PS:本文使用jdk1.7解析1.Object類 的equals 方法 複製程式碼程式碼如下:    /**      * Indicates whether some other object is "equal to" thi

深度解讀equal方法hashCode方法淵源

深度解讀equal方法與hashCode方法淵源 大部分內容參考自重寫equal()時為什麼也得重寫hashCode()之深度解讀equal方法與hashCode方法淵源 1. equals()的所屬以及內部原理(即Object中equals方法的實現原理) 說起equals方法

深入解析Axios 常用的請求方法別名

下面小編就為大家分享一篇Axios 常用的請求方法別名,寫的十分的全面細緻,具有一定的參考價值,對此有需要的朋友可以參考學習下。如有不足之處,歡迎批評指正。 Axios 是一個基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。常用的請求方法別名一般有: Get/pos

hashCode()方法equal()方法

1  equals:         如果不重寫,則呼叫的Object的equals方法,Object.equals方法運用的‘==’,比較的是記憶體地址;        &nbs

深入解析JQuery中的isPlainObject()使用方法

本篇文章給大家詳細分析了jQuery中的isPlainObject()使用方法,寫的十分的全面細緻,具有一定的參考價值,對此有需要的朋友可以參考學習下。如有不足之處,歡迎批評指正。 說明 jQuery中的isPlainObject() 函式用於判斷指定引數是否是一個純粹的物

Hibernate的Session介面中save/delete/update方法2個引數(entityName)的深入解析

Hibernate的Session介面中delete/update方法2個引數(entityName)的深入解析 2010-09-14 18:07 session.update(Object arg0); session.update(String arg0, Obje

重寫equal()時為什麼也得重寫hashCode()之深度解讀equal方法hashCode方法淵源

轉載請註明出處: 今天這篇文章我們打算來深度解讀一下equal方法以及其關聯方法hashCode(),我們準備從以下幾點入手分析: 1.equals()的所屬以及內部原理(即Object中equals方法的實現原理) 說起equals方法,我們都知道是超類Obje

String用法詳解(equal原始碼 ==和equal的解釋、字面賦值和new賦值效率、重寫了hashcode方法解釋)

String  a = “abc”;//在字串池中找abc,如果有,就直接返回地址,如果沒有就加值abc然後再返回地址(此方式的值是存放在字串池中) String  b =  “abc”; String  c  =   new String("abc");//在字串池中找a

深入解析jQuery $.each的使用方法

$(function () {     /*長條導航*/     $("#lmlbl  li:eq(0)").attr("class", "cll sell");     $("#lmnrl  div:eq(0)").show();     $("#lmlbl li").each(function (i) {

深入解析Java反射-invoke方法

上篇文章中回顧了一下Java反射相關的基礎內容。這一節我們來深入研究Method類中的invoke方法,探尋它的奧祕。注:本篇文章的所有原始碼都基於OpenJDK 1.8。 引入即使沒有學過反射,大家也一定會見過invoke方法。因為很多方法呼叫都是靠invoke方法,所以很多異常的丟

深入解析Javascript閉包及實現方法

開發 選擇 屬性。 ror pla 類名 資料 允許 {} 一、什麽是閉包和閉包的幾種寫法和用法 1、什麽是閉包 閉包,官方對閉包的解釋是:一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。閉包的特點:1. 作為一個函數變量

深入解析 C# 的 String.Create 的方法

> 作者:Casey McQuillan > > 譯者:精緻碼農 > > 原文:http://dwz.win/YVW > > 說明:原文比較長,翻譯時精簡了很多內容,對於不重要的細枝末節只用了一句話概括,但不併影響閱讀。 你還記得上一次一個無足輕重的細節點燃你思考火花的時刻嗎?作為一個軟體工程師,我習慣於專

深入解析 C# 的 String.Create 方法

> 作者:Casey McQuillan > > 譯者:精緻碼農 > > 原文:http://dwz.win/YVW > > 說明:原文比較長,翻譯時精簡了很多內容,對於不重要的細枝末節只用了一句話概括,但不併影響閱讀。 你還記得上一次一個無足輕重的細節點燃你思考火花的時刻嗎?作為一個軟體工程師,我習慣於專

MySQL主從復制原理深入解析與練習

數據庫 記錄 sql語句 change master MySQL主從復制原理深入解析與練習MySQL主從復制畫圖描述:MySQL主從復制原理上圖詳解:① 用戶做crud操作,寫入數據庫,更新結果記錄到binlog中;② 主從同步是主找從的,從庫IO發起請求,主庫的主進程看從庫的master

深入解析瀏覽器的幕後工作原理(三) 呈現樹和 DOM 樹的關系

文本 一行 出現 src 格式 關於 放置 顯示 關系 呈現樹和 DOM 樹的關系   呈現器是和 DOM 元素相對應的,但並非一一對應。非可視化的 DOM 元素不會插入呈現樹中,例如“head”元素。如果元素的 display 屬性值為“none”,那麽也不會顯示在呈現

深入解析瀏覽器的幕後工作原理(二) 呈現引擎

div 分享 image ima 好的 clas logs 指令 開放源代碼 呈現引擎   本文所討論的瀏覽器(Firefox、Chrome 瀏覽器和 Safari)是基於兩種呈現引擎構建的。Firefox 使用的是 Gecko,這是 Mozilla 公司“自制”的呈現

jQuery技術內幕:深入解析jQuery架構設計與實現原理

源碼 att root 功能 技術內幕 瀏覽器 sel 緩存 callbacks jQuery源碼(jquery-1.7.1.js)的總體結構:(function( window, undefined ) {// 構造jQuery對象 var jQuery = (fun

js學習總結----call方法深入

pro 是我 學習總結 type 模擬 eva -- tex span      var obj = {name:"張三"} function fn(){ console.log(this) } fn

java int轉String全部方式的效率對照與深入解析

表達 comm 個數 第一個 另一個 alt lock his ng-   在java中,大家肯定都會遇到int類型轉String類型的情形,知其然知其所以然。總結加分析一下,int類型轉String類型有下面幾種方式:  a+”“String.valu