HashMap中使用自定義類做爲Key時,爲什麼要重寫HashCode和Equals方法

以前一直不是很理解爲何要重寫HashCode和Equals方法,才只能做爲鍵值存儲在HashMap中。經過下文,能夠一探究竟。java

 

首先,若是咱們直接用如下的Person類做爲鍵,存入HashMap中,會發生髮生什麼狀況呢?segmentfault

 

public class Person {ide

 

    private String id;this

 

    public Person(String id) {spa

        this.id = id;對象

    }字符串

}get

import java.util.HashMap;hash

 

public class Main {class

    public static void main(String[] args) {

 

        HashMap<Person, String> map = new HashMap<Person, String>();

        map.put(new Person("001"), "findingsea");

        map.put(new Person("002"), "linyin");

        map.put(new Person("003"), "henrylin");

        map.put(new Person("003"), "findingsealy");

 

        System.out.println(map.toString());

 

        System.out.println(map.get(new Person("001")));

        System.out.println(map.get(new Person("002")));

        System.out.println(map.get(new Person("003")));

    }

}

那麼輸出結果是什麼呢?

{Person@6e4d4d5e=henrylin, Person@275cea3=findingsea, Person@15128ee5=findingsealy, Person@4513098=linyin}

null

null

null

 

咱們能夠看到,這裏出現了兩個問題:

  1. 在添加的過程當中,咱們將key=new Person("003")的鍵值對添加了兩次,那麼在指望中,HashMap中應該只存在一對這樣的鍵值對,由於key(指望中)是相同的,因此不該該重複添加,第二次添加的value="findingsealy"應該替換掉原先的value="henrylin"。可是在輸入中,咱們發現指望中的狀況並無出現,而是在HashMap同時存在了value="findingsealy"和value="henrylin"的兩個鍵值對,而且它們的key值仍是不相同的,這顯然是錯誤的。
  2. 在獲取value值時,咱們分別用三個Person對象去查找,這三個對象和咱們剛剛存入的三個key值(在指望中)是相同的,可是查找出的倒是三個null值,這顯然也是錯誤的。

那麼,正確的方法其實在不少地方都是被描述過了,直接對Person類進行修改,重載equals和hashCode方法,修改事後的Person類以下:

public class Person {

 

    private String id;

 

    public Person(String id) {

        this.id = id;

    }

 

    @Override

    public boolean equals(Object o) {

        if (this == o) return true;

        if (o == null || getClass() != o.getClass()) return false;

 

        Person person = (Person) o;

 

        if (id != null ? !id.equals(person.id) : person.id != null) return false;

 

        return true;

    }

 

    @Override

    public int hashCode() {

        return id != null ? id.hashCode() : 0;

    }

}

那麼,當咱們從新執行上述的檢驗程序時,獲得的結果以下:

{Person@ba31=findingsea, Person@ba32=linyin, Person@ba33=findingsealy}

findingsea

linyin

findingsealy

能夠看到,以前指出的亮點錯誤都獲得了改正。那麼,爲何會這樣呢?

在HashMap中,查找key的比較順序爲:

  1. 計算對象的Hash Code,看在表中是否存在。
  2. 檢查對應Hash Code位置中的對象和當前對象是否相等。

顯然,第一步就是要用到hashCode()方法,而第二步就是要用到equals()方法。在沒有進行重載時,在這兩步會默認調用Object類的這兩個方法,而在Object中,Hash Code的計算方法是根據對象的地址進行計算的,那兩個Person("003")的對象地址是不一樣的,因此它們的Hash Code也不一樣,天然HashMap也不會把它們當成是同一個key了。同時,在Object默認的equals()中,也是根據對象的地址進行比較,天然一個Person("003")和另外一個Person("003")是不相等的。

理解了這一點,就很容易搞清楚爲何須要同時重載hashCode()和equals兩個方法了。

  • 重載hashCode()是爲了對同一個key,能獲得相同的Hash Code,這樣HashMap就能夠定位到咱們指定的key上。
  • 重載equals()是爲了向HashMap代表當前對象和key上所保存的對象是相等的,這樣咱們才真正地得到了這個key所對應的這個鍵值對。

還有一個細節,在Person類中對於hashCode()的重在方法爲:

@Override

public int hashCode() {

    return id != null ? id.hashCode() : 0;

}

這裏可能有疑惑的點在於:爲何能夠用String類型的變量的Hash Code做爲Person類的Hash Code值呢?這樣new Person(new String("003"))和new Person(new String("003"))的Hash Code是相等的嗎?

來看看如下代碼的輸出:

System.out.println("findingsea".hashCode());

System.out.println("findingsea".hashCode());

System.out.println(new String("findingsea").hashCode());

System.out.println(new String("findingsea").hashCode());

728795174

728795174

728795174

728795174

能夠看到四條語句的輸出都是相等的,很直觀的合理的猜想就是String類型也重載了hashCode()以根據字符串的內容來返回Hash Code值,因此相同內容的字符串具備相同的Hash Code。

同時,這也說明了一個問題:爲何在已知hashCode()相等的狀況下,還須要用equals()進行比較呢?就是由於避免出現上述例子中的出現的狀況,由於根據對Person類的hashCode()方法的重載實現,Person類會直接用id這個String類型成員的Hash Code值做爲本身的Hash Code值,可是很顯然的,一個Person("003")和一個String("003")是不相等的,因此在hashCode()相等的狀況下,還須要用equals()進行比較。

如下例子能夠做爲上述說明的佐證:

System.out.println(new Person("003").hashCode()); // 47667

System.out.println(new String("003").hashCode()); // 47667

 

System.out.println(new Person("003").equals(new String("003"))); // false

以上便是所有。

 參考:https://segmentfault.com/a/1190000002655085

相關文章
相關標籤/搜索