我們知道,equals()函數是用來做比較的。java中的比較有兩種:一種是內存地址的比較,一種是內容的比較。而比較個體也有兩種:一種是簡單類型(這類簡單說來無所謂內存地址的比較或者內容比較的區別);還有一種是對象的比較,本文中說的主要是后者

在java中,(對象)內存地址的比較,是通過==完成的。比如 

if (obj1  ==  obj2) {
    ...
}

這樣的語句中,我們認為,如果obj1和obj2的內存地址相同,則返回true

而equals()通常是比較內容的。這里說“通常” ,是因為在最根本的Object類中,equal()函數做的是地址的比較。而在其他幾乎所有的類中,equals()都經過重載,進行內容的比較。

而在說equals()的時候我們還涉及hashCode()是因為在有些應用中(比如,HashMap的key是對象),必須在重載equals()的同時重載hashCode()。因為java中默認(Object)的hashCode是根據對象的地址計算得到的。

我們通常不會注意到這個問題,因為我們通常所使用的key都是簡單類型,或者是String, Long等一些特殊的對象(其特殊性請參看筆者在寫java 淺拷貝和深拷貝時的討論),這時候,這個問題被我們無意間繞過了

有人已經概括了這種我們忽略了的情況:“如果你想將一個對象A放入另一個收集(集合)對象B里,或者使用這個對象A為查找一個元對象在收集對 象B里位置的鑰匙(key),并支持是否容納(isContains()),刪除收集對象B里的元對象(remove()?)這樣的操作,那么,equals()和hashCode()函數必須開發者自己定義。” (括號為筆者添加)

為了便于理解,舉一段程序為例:

import  java.util. * ;
public   class  Person {

int  id;
String name;

// define getter and setter here, omited

public  Person( int  id, String name) {
     
this .id  =  id;
     
this .name  =  name;
     
}


public   boolean  equals(Object o) {
       
if ( this == o)  return   true ;
    
if (o  instanceof  Person)
    
return  ( this .id  ==  ((Person)o).id)  &&  ( this .name.equals(((Person)o).name));
    }


/*          
public int hashCode(){
    return id*37;
}
*/


public   static   void  main(String args[])  {
    Person p1 
=   new  Person( 1 , " aaa " );
    Person p2 
=   new  Person( 1 , " aaa " );
    Map map 
=   new  HashMap();

    map.put(p2,p1);
    Person value 
=  (Person)map.get(p1);
    System.out.println(value.name);
  }

}

這段代碼的結果是什么?答案是nullPointerExcetpion.

而把hashCode()的注釋去除,程序就可以返回正確的結果了。為什么呢?因為:

Map.put(key,value)時根據key.hashCode生成一個內部hash值,根據這個hash值將對象存放在一個table中

Map.get(key)會比較key.hashCode和equals方法,當且僅當這兩者相等時,才能正確定位到table。而我們說過,默認的java是對地址進行比較的。