hashcode and equals

java.lnag.Object中對hashCode的約定:

  1. 在一個應用程序執行期間,如果一個對象的equals方法做比較所用到的信息沒有被修改的話,則對該對象調用hashCode方法多次,它必須始終如一地返回同一個整數。
  2. 如果兩個對象根據equals(Object o)方法是相等的,則調用這兩個對象中任一對象的hashCode方法必須產生相同的整數結果。
  3. 如果兩個對象根據equals(Object o)方法是不相等的,則調用這兩個對象中任一個對象的hashCode方法,不要求產生不同的整數結果。但如果能不同,則可能提高散列表的性能。

看個不改寫hashCode導致使用hashMap不能出現預期結果的例子:
public final class PhoneNumber{
private final short areaCode;
private final short exchange;
private final short extension;

public PhoneNumber(int areaCode,int exchage,int extension){
rangeCheck(areaCode,999,"area code");
rangeCheck(exchange,999,"exchange");
rangeCheck(extension,9999,"extension");
this.areaCode=(short) areaCode;
this.exchange=(short) exchange;
this.extension=(short)extension;
}
private static void rangeCheck(int arg,int max, String name){
if(arg<0 || arg>max) throw new IllegalArgumentException(name+":"+arg);
}
public boolean equals(Object o){
if (o == this) reutrn true;
if (!(o instanceof PhoneNumber)) return false;
PhoneNumber pn=(PhoneNumber)o;
return pn.extension==extension && pn.exchange=exchange && pn.areaCode=areaCode;
}
//No hashCode method
...
}
現在有以下幾行程序:
Map m=new HashMap();
m.put(new PhoneNumber(1,2,3),"Jenny");
則m.get(new PhoneNumber(1,2,3))的返回值什麼?
  雖然這個實例據equals是相等的,但由於沒改寫hashCode而致兩個實例的散列碼並不同(即違反第二條要求),因則返回的結果是null 而不是"Jenny".
  理想情況下,一個散列函數應該把一個集合中不相等的實例均勻地分佈到所有可能的散列值上,下面是接近理想的“處方”:

 

  1. 把某個非零常數值(如17)保存在一個叫result的int類型的變量中;
  2. 對於對象中每個關鍵字域f(指equals方法中考慮的每一個域),完成以下步驟:
    1. 爲該域計算int類型的散列碼c:
      1. 如果該域是bloolean類型,則計算(f?0:1)
      2. 如果該域是byte,char,short或int類型,則計算(int)f
      3. 如果該域是long類型,則計算(int)(f^(>>>32))
      4. 如果該域是float類型,則計算Float.floatToIntBits(f)
      5. 如果該域是double類型,則計算Double.doubleToLongBits(f)得一long類型值,然後按前述計算此long類型的散列值
      6. 如果該域是一個對象引用,則利用此對象的hashCode,如果域的值爲null,則返回0
      7. 如果該域是一個數組,則對每一個數組元素當作單獨的域來處理,然後安下一步的方案來進行合成


    2. 利用下面的公式將散列碼c 組合到result中。

              result=37*result+c;


  3. 檢查“相等的實例是否具有相等的散列碼?”,如果爲否,則修正錯誤。

  依照這個處方,得PhoneNumber的hashCode方法:
public int hashCode(){
int result=17;
result=37*result+areaCode;
result=37*result+exchange;
result=37*result+extension;
return result;
}
  如果計算散列碼的代價比較高,可以考慮用內部保存這個碼,在創建是生成或遲緩初始化生成它。不要試圖從散列碼計算中排除掉一個對象的關鍵部分以提高性能。
1.hashCode()方法使用來提高Map裏面的搜索效率的,Map會根據不同的hashCode()來放在不同的桶裏面,Map在搜索一 個對象的時候先通過hashCode()找到相應的桶,然後再根據equals()方法找到相應的對象.要正確的實現Map裏面查找元素必須滿足一下兩個 條件:
(1)當obj1.equals(obj2)爲true時obj1.hashCode()   ==   obj2.hashCode()必須爲true
(2)當obj1.hashCode()   !=   obj2.hashCode()爲true時obj.equals(obj2)必須爲false

2.一個類實現Comparable接口是用來決定該類對象的自然順序,這樣把該類對象放入TreeSet,TreeMap,時會根據該類對象的自然順序自動進行排序,另外往Queue裏面存的時候也會根據對象自然順序排序

3.光實現Comparable只能決定該類對象自然順序,但是很多時候需要自定義排序規則,那麼就要實現
Comparator 接口,例如一個People實現了Comparable接口並且定義自然順序爲按照名字字母順序排序,那麼把People存到List裏面的時候可以用 Collections.sort()方法根據自然順序對該List進行排序,問題就在於我現在要把People按照年齡排序該怎麼辦呢?一個類實現了 Comparable只能重寫compareTo()方法一次,那麼也只能有一種順序--自然順序,但是爲了可以用age大小排序這個List的話,就必 須使用一個類實現Comparator並且重寫compare()方法,定義按照age排序的規則,然後調用Collections   的   static   <T>   void   sort(List <T>   list,   Comparator <?   super   T>   c)   這個方法來按照指定比較器規定的順序來對List排序
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章