java中equals,hashcode和==的區別

一、==html

java中的數據類型,可分爲兩類:java

1.基本數據類型,也稱原始數據類型程序員

byte,short,char,int,long,float,double,boolean   他們之間的比較,應用雙等號(==),比較的是他們的值。 算法

2.引用類型(類、接口、數組)   數組

當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,因此,除非是同一個new出來的對象,他們的比較後的結果爲true,不然比較後結果爲false。緩存

對象是放在堆中的,棧中存放的是對象的引用(地址)。因而可知'=='是對棧中的值進行比較的。若是要比較堆中對象的內容是否相同,那麼就要重寫equals方法了。 性能優化

例:app

 

[java] view plain copy函數

  1. public static void main(String[] args) {  
  2.   
  3.         int int1 = 12;  
  4.         int int2 = 12;  
  5.         Integer Integer1 = new Integer(12);  
  6.         Integer Integer2 = new Integer(12);  
  7.         Integer Integer3 = new Integer(127);  
  8.   
  9.         Integer a1 = 127;  
  10.         Integer b1 = 127;  
  11.   
  12.         Integer a = 128;  
  13.         Integer b = 128;  
  14.   
  15.         String s1 = "str";  
  16.         String s2 = "str";  
  17.         String str1 = new String("str");  
  18.         String str2 = new String("str");  
  19.   
  20.         System.out.println("int1==int2:" + (int1 == int2));  
  21.         System.out.println("int1==Integer1:" + (int1 == Integer1));  
  22.         System.out.println("Integer1==Integer2:" + (Integer1 == Integer2));  
  23.         System.out.println("Integer3==b1:" + (Integer3 == b1));  
  24.         System.out.println("a1==b1:" + (a1 == b1));  
  25.         System.out.println("a==b:" + (a == b));  
  26.           
  27.   
  28.         System.out.println("s1==s2:" + (s1 == s2));  
  29.         System.out.println("s1==str1:" + (s1 == str1));  
  30.         System.out.println("str1==str2:" + (str1 == str2));  
  31.   
  32.     }  

 

 

 

輸出結果:性能

int1==int2:true
int1==Integer1:true //Integer會自動拆箱爲int,因此爲true
Integer1==Integer2:false//不一樣對象,在內存存放地址不一樣,因此爲false
Integer3==b1:false//Integer3指向new的對象地址,b1指向緩存中127地址,地址不一樣,因此爲false

a1==b1:true
a==b:false

s1==s2:true
s1==str1:false
str1==str2:false

 

Integer b1 = 127;java在編譯的時候,被翻譯成-> Integer b1 = Integer.valueOf(127);

 

[java] view plain copy

  1. public static Integer valueOf(int i) {  
  2.          assert IntegerCache.high >= 127;  
  3.          if (i >= IntegerCache.low && i <= IntegerCache.high)  
  4.              return IntegerCache.cache[i + (-IntegerCache.low)];  
  5.          return new Integer(i);  
  6.      }  


看一下源碼你們都會明白,對於-128到127之間的數,會進行緩存,Integer b1 = 127時,會將127進行緩存,下次再寫Integer i6 = 127時,就會直接從緩存中取,就不會new了。因此a1==b1:true  a==b:false

 

 

 

二、equals

一、默認狀況(沒有覆蓋equals方法)下equals方法都是調用Object類的equals方法,而Object的equals方法主要用於判斷對象的內存地址引用是否是同一個地址(是否是同一個對象)。下面是Object類中equals方法:

[java] view plain copy

  1. public boolean equals(Object obj) {  
  2.     return (this == obj);  
  3.     }  

定義的equals與==是等效的

2 、要是類中覆蓋了equals方法,那麼就要根據具體的代碼來肯定equals方法的做用了,覆蓋後通常都是經過對象的內容是否相等來判斷對象是否相等。下面是String類對equals進行了重寫:

 

 

[java] view plain copy

  1. public boolean equals(Object anObject) {  
  2.     if (this == anObject) {  
  3.         return true;  
  4.     }  
  5.     if (anObject instanceof String) {  
  6.         String anotherString = (String)anObject;  
  7.         int n = count;  
  8.         if (n == anotherString.count) {  
  9.         char v1[] = value;  
  10.         char v2[] = anotherString.value;  
  11.         int i = offset;  
  12.         int j = anotherString.offset;  
  13.         while (n-- != 0) {  
  14.             if (v1[i++] != v2[j++])  
  15.             return false;  
  16.         }  
  17.         return true;  
  18.         }  
  19.     }  
  20.     return false;  
  21.     }  


即String中equals方法判斷相等的步驟是:

 

1.若A==B 便是同一個String對象 返回true

2.若對比對象是String類型則繼續,不然返回false

3.判斷A、B長度是否同樣,不同的話返回false

4。逐個字符比較,如有不相等字符,返回false

 

這裏對equals從新須要注意五點:
1   自反性:對任意引用值X,x.equals(x)的返回值必定爲true. 
2   對稱性:對於任何引用值x,y,當且僅當y.equals(x)返回值爲true時,x.equals(y)的返回值必定爲true; 
3   傳遞性:若是x.equals(y)=true, y.equals(z)=true,則x.equals(z)=true 
4   一致性:若是參與比較的對象沒任何改變,則對象比較的結果也不該該有任何改變 
5   非空性:任何非空的引用值X,x.equals(null)的返回值必定爲false 

 

實現高質量equals方法的訣竅: 
1.使用==符號檢查「參數是否爲這個對象的引用」。若是是,則返回true。這只不過是一種性能優化,若是比較操做有可能很昂貴,就值得這麼作。 
2.使用instanceof操做符檢查「參數是否爲正確的類型」。若是不是,則返回false。通常來講,所謂「正確的類型」是指equals方法所在的那個類。 
3.把參數轉換成正確的類型。由於轉換以前進行過instanceof測試,因此確保會成功。 
4.對於該類中的每一個「關鍵」域,檢查參數中的域是否與該對象中對應的域相匹配。若是這些測試所有成功,則返回true;不然返回false。 
5.當編寫完成了equals方法以後,檢查「對稱性」、「傳遞性」、「一致性」。 

 

三、hashCode

hashCode()方法返回的就是一個數值,從方法的名稱上就能夠看出,其目的是生成一個hash碼。hash碼的主要用途就是在對對象進行散列的時候做爲key輸入,據此很容易推斷出,咱們須要每一個對象的hash碼儘量不一樣,這樣才能保證散列的存取性能。事實上,Object類提供的默認實現確實保證每一個對象的hash碼不一樣(在對象的內存地址基礎上通過特定算法返回一個hash碼)。Java採用了哈希表的原理。哈希(Hash)其實是我的名,因爲他提出一哈希算法的概念,因此就以他的名字命名了。 哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上。初學者能夠這樣理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並非)。   

散列函數,散列算法,哈希函數。
是一種從任何一種數據中建立小的數字「指紋」的方法。
散列函數將任意長度的二進制值映射爲較短的固定長度的二進制值,這個小的二進制值稱爲哈希值。
好的散列函數在輸入域中不多出現散列衝突。
=================================================================================
全部散列函數都有以下一個基本特性:
1:若是a=b,則h(a) = h(b)。
2:若是a!=b,則h(a)與h(b)可能獲得相同的散列值。

Object 的hashCode方法:返回一個int類型

[java] view plain copy

  1. public native int hashCode();  


3.1 hashCode的做用

 

想要明白,必需要先知道Java中的集合。   
總的來講,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。前者集合內的元素是有序的,元素能夠重複;後者元素無序,但元素不可重複。

那麼這裏就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢? 

這就是Object.equals方法了。可是,若是每增長一個元素就檢查一次,那麼當元素不少時,後添加到集合中的元素比較的次數就很是多了。也就是說,若是集合中如今已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大下降效率。   
因而,Java採用了哈希表的原理。  

 

這樣一來,當集合要添加新的元素時,

先調用這個元素的hashCode方法,就一會兒能定位到它應該放置的物理位置上。 

若是這個位置上沒有元素,它就能夠直接存儲在這個位置上,不用再進行任何比較了;

若是這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存,不相同就散列其它的地址。因此這裏存在一個衝突解決的問題。這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次。

 

四、eqauls方法和hashCode方法關係

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

(1)同一對象上屢次調用hashCode()方法,老是返回相同的整型值。

(2)若是a.equals(b),則必定有a.hashCode() 必定等於 b.hashCode()。 

(3)若是!a.equals(b),則a.hashCode() 不必定等於 b.hashCode()。此時若是a.hashCode() 老是不等於 b.hashCode(),會提升hashtables的性能。

(4)a.hashCode()==b.hashCode() 則 a.equals(b)可真可假

(5)a.hashCode()!= b.hashCode() 則 a.equals(b)爲假。 

 

上面結論簡記:

一、若是兩個對象equals,Java運行時環境會認爲他們的hashcode必定相等。 
二、若是兩個對象不equals,他們的hashcode有可能相等。 
三、若是兩個對象hashcode相等,他們不必定equals。 
四、若是兩個對象hashcode不相等,他們必定不equals。 

 

關於這兩個方法的重要規範: 

規範1:若重寫equals(Object obj)方法,有必要重寫hashcode()方法,確保經過equals(Object obj)方法判斷結果爲true的兩個對象具有相等的hashcode()返回值。說得簡單點就是:「若是兩個對象相同,那麼他們的hashcode應該相等」。不過請注意:這個只是規範,若是你非要寫一個類讓equals(Object obj)返回true而hashcode()返回兩個不相等的值,編譯和運行都是不會報錯的。不過這樣違反了Java規範,程序也就埋下了BUG。 

規範2:若是equals(Object obj)返回false,即兩個對象「不相同」,並不要求對這兩個對象調用hashcode()方法獲得兩個不相同的數。說的簡單點就是:「若是兩個對象不相同,他們的hashcode可能相同」。 

 

五、爲何覆蓋equals時總要覆蓋hashCode 
 一個很常見的錯誤根源在於沒有覆蓋hashCode方法。在每一個覆蓋了equals方法的類中,也必須覆蓋hashCode方法。若是不這樣作的話,就會違反Object.hashCode的通用約定,從而致使該類沒法結合全部基於散列的集合一塊兒正常運做,這樣的集合包括HashMap、HashSet和Hashtable。

1.在應用程序的執行期間,只要對象的equals方法的比較操做所用到的信息沒有被修改,那麼對這同一個對象調用屢次,hashCode方法都必須始終如一地返回同一個整數。在同一個應用程序的屢次執行過程當中,每次執行所返回的整數能夠不一致。

2.若是兩個對象根據equals()方法比較是相等的,那麼調用這兩個對象中任意一個對象的hashCode方法都必須產生一樣的整數結果。

3.若是兩個對象根據equals()方法比較是不相等的,那麼調用這兩個對象中任意一個對象的hashCode方法,則不必定要產生相同的整數結果。可是程序員應該知道,給不相等的對象產生大相徑庭的整數結果,有可能提升散列表的性能。

 

六、總結:
一、equals方法用於比較對象的內容是否相等(覆蓋之後)

二、hashcode方法只有在集合中用到

三、當覆蓋了equals方法時,比較對象是否相等將經過覆蓋後的equals方法進行比較(判斷對象的內容是否相等)。

四、將對象放入到集合中時,首先判斷要放入對象的hashcode值與集合中的任意一個元素的hashcode值是否相等,若是不相等直接將該對象放入集合中。若是hashcode值相等,而後再經過equals方法判斷要放入對象與集合中的任意一個對象是否相等,若是equals判斷不相等,直接將該元素放入到集合中,不然不放入。

 

參考來源:

Integer與int的種種比較你知道多少?

java中hashCode方法與equals方法的用法總結

hashCode與equals的區別與聯繫

equals和hashCode解析

相關文章
相關標籤/搜索