1. 程式人生 > 其它 >hashmap有了hash為什麼還要equals_使用HashMap為什麼要重寫hashCode和equals

hashmap有了hash為什麼還要equals_使用HashMap為什麼要重寫hashCode和equals

技術標籤:hashmap有了hash為什麼還要equals

Hash演算法

在說HashMap之前先來了解一下Hash演算法。在資料結構中學習過線性表,我們知道線上性表中查詢一個值最壞的情況可能是從頭遍歷到尾,其平均時間複雜度為O(n),並不理想,而hash表能將查詢的時間複雜度降為O(1),因為Hash演算法會通過hash函式計算出地址直接取值,其查詢次數只有一次。

通過下面例子簡單瞭解一下hash表的查詢方式,下面是一個hash表,首先假設hash函式為n%10,hash函式計算出來的結果就是其hash表中該元素的地址,所以10、13和26的儲存結果如下圖。

4e833ee9e32dea90bd5f821c339105da.png

可能實際的hash函式能很好地避免地址的衝突,但是還是有地址衝突的可能性,比如10和20。java中hashMap解決方式是"鏈地址法",如下圖,將hash值衝突的值使用連結串列連線起來,這樣查詢到地址0的時候就會依次比較10和20,看到底哪個才是要找的。

bb73c83dedfe3fd92191b6c2e1a8a949.png

HashMap

下面來了解什麼是HashMap,"**Map集合即Key-Value的集合,前面加個Hash,即雜湊,無序的。所以HashMap即散著的,無序的Key-Value集合**",這是最初我看到的一個對我個人而言比較好理解的解釋。當我們使用的hashMap的鍵值為物件的時候可能就要重寫hashCode和eqals。

先看下面一段程式碼

public class User {private String name;private String password;public User() {super();}public User(String name, String passed) {super();this.name = name;this.passed = passed;}public class Demo {public static void main(String[] args) {User user1=new User("name", "passed");User user2=new User("name", "passed");//定義hashMapHashMap hashmap=new HashMap();//新增hashmap.put(user1, "value1");//通過user2獲取新增的valueString str=hashmap.get(user2);System.out.println("輸出結果:"+str);}}

通過程式碼可知,實體類User中只有兩個屬性name和password,Main函式中聲明瞭兩個User的例項,他們的兩個屬性都是相同的,那我們現在希望使用user2取出user1對應的value值,看下是否能成功。

執行結果:

輸出結果:null

為什麼不是想象的結果呢。因為當我們向hashmap新增user1時,hashmap首先會呼叫User的hashCode來計算hash值作為地址,因為本例中沒有重寫hashCode方法,所以hashmap是呼叫的Object的hashCode方法來計算hash值,Object中hashCode計算出來的hash值其實就是物件的地址,所以user1與user2儲存的的地址肯定不同,下面就重寫User的hashCode

@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + ((name == null) ? 0 : name.hashCode());result = prime * result + ((passed == null) ? 0 : passed.hashCode());return result;}

執行結果:

輸出結果:null

執行結果還是null,既然重寫了hashCode方法,尋找user2時候理論上是能夠正確尋找到user1儲存地址的,為什麼結果還是null?這裡就要了解一下HashMap找到地址後動作。前面已經說過,java中HashMap解決hash值衝突,使用了鏈地址法,也就是在通過user2獲取user1的value的時候並不是通過User重寫的hashCode計算出user2的地址後就直接從該地址中取相應的值,而是還要呼叫equals方法來進行比較,這就和沒有重寫hashCode造成錯誤的原因類似了,沒有重寫equals方法,就要被迫呼叫Object類的equals方法,而Object類的equals方法是直接比較兩個物件的記憶體地址,所以輸出結果是null。

現在重寫equals方法

@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;User other = (User) obj;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;if (passed == null) {if (other.passed != null)return false;} else if (!passed.equals(other.passed))return false;return true;}

執行結果:

輸出結果:value1

成功。

總結

由上面的分析可知,當鍵值為物件型別的時候就需要重寫hashCode和equals方法。