set.put(new Student(1,"aa") );
set.put(new Student(1,"aa") );
set.put(new Student(2,"aa") );
結果set內的元素為3個,沒有去處重復的new Student(1,"aa") ?為什么呢?
這里由於兩個new Student(1,"aa")是不一樣的Student對象。而默認的Student類的hashcode是根據對象的引用算的。所以直接認為是兩個不一樣的對象,直接put進去了。所以需要重寫hashcode方法,如果hashcode不一樣則直接認為是不同對象,如下:
-
class Student {
-
private int code;
-
private Stirng name;
-
public int hashcode() {
-
return code*name.hashcode();
-
}
-
}
發現還是不對,還是put進去了呢?
這里重寫的hashcode是一樣的,所以還是put進去了。所以還需要重新equals方法。其實是有這樣一個規定,如果hahscode一樣時,則還需要繼續調用equals方式看看對象是否相等。如下即可實現:
-
public boolean equals(Object o) {
-
Student s = (Student ) o;
-
if (name.equals(s.getName) && code == o.getCode()) {
-
return true;
-
}
-
return false;
-
}
可以看到如果hashcode不一樣就直接認為是不一樣的對象,不需要再去equal比較,更加節省時間。
如果new Student(1,"aa")、new Student(1,"bb")。通過code和name算出的hashcode就可以算是不一樣的對象,就不需要再去equals比較。
往往HashSet中存放的對象是否相等的邏輯都需要自己定義,而並不會直接用默認的引用來算,即一般都會重新hashcode和equals方法,而且同時需要重寫。以后要注意哦。
HashMap的put和get也類似。
HashMap是底層實現時數組加鏈表。
A.當put元素時:
1.首先根據put元素的key獲取hashcode,然后根據hashcode算出數組的下標位置,如果下標位置沒有元素,直接放入元素即可。
2.如果該下標位置有元素(即根據put元素的key算出的hashcode一樣即重復了),則需要已有元素和put元素的key對象比較equals方法,如果equals不一樣,則說明可以放入進map中。這里由於hashcode一樣,所以得出的數組下標位置相同。所以會在該數組位置創建一個鏈表,后put進入的元素到放鏈表頭,原來的元素向后移動。
B.當get元素時:
根據元素的key獲取hashcode,然后根據hashcode獲取數組下標位置,如果只有一個元素則直接取出。如果該位置一個鏈表,則需要調用equals方法遍歷鏈表中的所有元素與當前的元素比較,得到真正想要的對象。
可以看出如果根據hashcdoe算出的數組位置盡量的均勻分布,則可以避免遍歷鏈表的情況,以提高性能。
所以要求重寫hashmap時,也要重寫equals方法。以保證他們是相同的比較邏輯。
在一個應用程序執行期間,如果一個對象的equals方法做比較所用到的信息沒有被修改的話,那么,對該對象調用hashCode方法多次,它必須始終如一地返回 同一個整數。在同一個應用程序的多次執行過程中,這個整數可以不同,即這個應用程序這次執行返回的整數與下一次執行返回的整數可以不一致。
如果兩個對象根據equals(Object)方法是相等的,那么調用這兩個對象中任一個對象的hashCode方法必須產生同樣的整數結果。
如果兩個對象根據equals(Object)方法是不相等的,那么調用這兩個對象中任一個對象的hashCode方法,不要求必須產生不同的整數結果。然而,程序員應該意識到這樣的事實,對於不相等的對象產生截然不同的整數結果,有可能提高散列表(hash table)的性能。
如果只重寫了equals方法而沒有重寫hashCode方法的話,則會違反約定的第二條:相等的對象必須具有相等的散列碼(hashCode)。
同時對於HashSet和HashMap這些基於散列值(hash)實現的類。HashMap的底層處理機制是以數組的方法保存放入的數據的(Node<K,V>[] table),其中的關鍵是數組下標的處理。數組的下標是根據傳入的元素hashCode方法的返回值再和特定的值異或決定的。如果該數組位置上已經有放入的值了,且傳入的鍵值相等則不處理,若不相等則覆蓋原來的值,如果數組位置沒有條目,則插入,並加入到相應的鏈表中。檢查鍵是否存在也是根據hashCode值來確定的。所以如果不重寫hashCode的話,可能導致HashSet、HashMap不能正常的運作、
如果我們將某個自定義對象存到HashMap或者HashSet及其類似實現類中的時候,如果該對象的屬性參與了hashCode的計算,那么就不能修改該對象參數hashCode計算的屬性了。有可能會移除不了元素,導致內存泄漏。