這是Java中很經典的問題,在面試中也常常被問起。其實不少書或者文章都提到過要重載hashCode()
和equals()
兩個方法才能實現自定義鍵在HashMap
中的查找,可是爲何要這樣以及若是不這樣作會產生什麼後果,好像不多有文章講到,因此寫這麼一篇來講明下。java
首先,若是咱們直接用如下的Person
類做爲鍵,存入HashMap
中,會發生髮生什麼狀況呢?面試
javapublic class Person { private String id; public Person(String id) { this.id = id; } }
javaimport java.util.HashMap; public class Main { 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"))); } }
那麼輸出結果是什麼呢?ide
{Person@6e4d4d5e=henrylin, Person@275cea3=findingsea, Person@15128ee5=findingsealy, Person@4513098=linyin} null null null
咱們能夠看到,這裏出現了兩個問題:this
在添加的過程當中,咱們將key=new Person("003")
的鍵值對添加了兩次,那麼在指望中,HashMap
中應該只存在一對這樣的鍵值對,由於key
(指望中)是相同的,因此不該該重複添加,第二次添加的value="findingsealy"
應該替換掉原先的value="henrylin"
。可是在輸入中,咱們發現指望中的狀況並無出現,而是在HashMap
同時存在了value="findingsealy"
和value="henrylin"
的兩個鍵值對,而且它們的key
值仍是不相同的,這顯然是錯誤的。code
在獲取value
值時,咱們分別用三個Person
對象去查找,這三個對象和咱們剛剛存入的三個key
值(在指望中)是相同的,可是查找出的倒是三個null
值,這顯然也是錯誤的。對象
那麼,正確的方法其實在不少地方都是被描述過了,直接對Person
類進行修改,重載equals
和hashCode
方法,修改事後的Person
類以下:字符串
javapublic 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; } }
那麼,當咱們從新執行上述的檢驗程序時,獲得的結果以下:get
{Person@ba31=findingsea, Person@ba32=linyin, Person@ba33=findingsealy} findingsea linyin findingsealy
能夠看到,以前指出的亮點錯誤都獲得了改正。那麼,爲何會這樣呢?hash
在HashMap
中,查找key
的比較順序爲:class
Hash Code
,看在表中是否存在。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()
的重在方法爲:
java@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
是相等的嗎?
來看看如下代碼的輸出:
javaSystem.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()
進行比較。
如下例子能夠做爲上述說明的佐證:
javaSystem.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
以上便是所有。