1. 程式人生 > >java中equals,hashcode和==的區別

java中equals,hashcode和==的區別

nat 等號 bsp alt star object 名稱 改變 set

1、==

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

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

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

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

當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,所以,除非是同一個new出來的對象,他們的比較後的結果為true,否則比較後結果為false。

對象是放在堆中的,棧中存放的是對象的引用(地址)。由此可見‘==‘是對棧中的值進行比較的。如果要比較堆中對象的內容是否相同,那麽就要重寫equals方法了。

例:


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

輸出結果:

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);


  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

2、equals

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


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

定義的equals與==是等效的

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


  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方法之後,檢查“對稱性”、“傳遞性”、“一致性”。

3、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類型


  1. public native int hashCode();


3.1 hashCode的作用

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

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

這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麽當元素很多時,後添加到集合中的元素比較的次數就非常多了。也就是說,如果集合中現在已經有1000個元素,那麽第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。
於是,Java采用了哈希表的原理。

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

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

如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;

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

4、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)為假。

上面結論簡記:

1、如果兩個對象equals,Java運行時環境會認為他們的hashcode一定相等。
2、如果兩個對象不equals,他們的hashcode有可能相等。
3、如果兩個對象hashcode相等,他們不一定equals。
4、如果兩個對象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可能相同”。

5、為什麽覆蓋equals時總要覆蓋hashCode
一個很常見的錯誤根源在於沒有覆蓋hashCode方法。在每個覆蓋了equals方法的類中,也必須覆蓋hashCode方法。如果不這樣做的話,就會違反Object.hashCode的通用約定,從而導致該類無法結合所有基於散列的集合一起正常運作,這樣的集合包括HashMap、HashSet和Hashtable。

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

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

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

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

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

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

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

轉載: http://blog.csdn.net/hla199106/article/details/46907725

java中equals,hashcode和==的區別