1、首先我們看看對象默認的(Object)的equals方法和hashcode方法
public boolean equals(Object obj) {
return(this== obj);
}
public native int hashCode();
對象在不重寫的情況下使用的是Object的equals方法和hashcode方法,從Object類的源碼我們知道,默認的equals 判斷的是兩個對象的引用指向的是不是同一個對象;而hashcode也是根據對象地址生成一個整數數值;
2、重寫equals
案例場景:
定義一個User對象有多個屬性值姓名、年齡、身份證;
我們寫代碼的時候會發現,兩個new出來的User()對象 無論他們的的各項值是否一樣兩個對象equals永遠都是false,兩個對象值完全一樣放到HashSet里面它會把這兩個值完全一樣的對象當成兩個不同的對象了,這樣的話好像HashSet的特性就丟失了;
其實原因就是我們沒有重寫User的equals方法,它會調用Object的equals方法,就如上圖一樣,Object的equals方法是比較對象的引用對象是否是同一個,兩個new出來的對象當然不一樣。
好了現在需求來了,我們需要兩個對象的各項屬性值一樣的就認為這兩個對象是相等的;那么此時我們就需要重寫equals方法了;
代碼如下
public classUser {
privateStringname;//姓名
privateStringIdCard;//身份證
private intage;//年齡
/**
* 重寫equals
*@paramobj
*@return
*/
@Override
@Override
public boolean equals(Object obj) {
if(obj ?instanceof ?User) {
User user = (User) obj;
if(user.getIdCard().equals(this.IdCard) && user.getName().equals(this.name) && user.getAge() ==this.age) {
return true;
}else{
return false;
}
}else{
return false;
}
}
//......省略N行代碼
}
那么現在關鍵的地方來了:現在我們重寫了User對象的equals方法,但并沒有重寫hashcode方法。
(1)首先測試下equals的正確性
User user1=newUser();
user1.setName("路西");
user1.setAge(18);
user1.setIdCard("430");
User user2=newUser();
user2.setName("路西");
user2.setAge(18);
user2.setIdCard("430");
System.out.println("user1.equals(user2)="+user1.equals(user2));
user1.equals(user2)測試結果為true;
(2)在兩個對象equals的情況下進行把他們分別放入Map和Set中
在上面的代碼基礎上追加如下代碼:
Set set =newHashSet();
set.add(user1);
set.add(user2);
Map map=newHashMap();
map.put(user1,"user1");
map.put(user2,"user2");
System.out.println("set 長度"+set.size());
System.out.println("map 長度"+map.keySet().size());;
測試打印結果為:
好了現在問題來了,明明user1和user2兩個對象是equals的那么為什么把他們放到set中會有兩個對象(Set特性是不允許重復數據的),還有Map也把兩個同樣的對象當成了不同的Key(Map的Key是不允許重復的,相同Key會覆蓋);
(3)這里我先拋出結果,至于原理后面再進行描述
原因是user1和user2的hashcode 不一樣導致的;
因為我們沒有重寫父類(Object)的hashcode方法,Object的hashcode方法會根據兩個對象的地址生成對相應的hashcode;
user1和user2是分別new出來的,那么他們的地址肯定是不一樣的,自然hashcode值也會不一樣。
Set區別對象是不是唯一的標準是,兩個對象hashcode是不是一樣,再判定兩個對象是否equals;
Map 是先根據Key值的hashcode分配和獲取對象保存數組下標的,然后再根據equals區分唯一值(詳見下面的map分析)
3、重寫hashcode方法;
public classUser? {
privateStringname;//姓名
privateStringIdCard;//身份證
private intage;//年齡
* 重寫equals
*@paramobj
*@return
*/
@Override
public boolean equals(Object obj) {
if(obj ?instanceof ?User) {
User user = (User) obj;
if(user.getIdCard().equals(this.IdCard) && user.getName().equals(this.name) && user.getAge() ==this.age) {
return true;
}else{
return false;
}
}else{
return false;
}
}
@Override
public int hashCode() {
intresult =name.hashCode();
result =31* result +IdCard.hashCode();
result =31* result +age;
returnresult;
}
//......省略N行代碼
}
我們按之前的流程重新測試一遍結果:
User user1=newUser();
user1.setName("路西");
user1.setAge(18);
user1.setIdCard("430");
User user2=newUser();
user2.setName("路西");
user2.setAge(18);
user2.setIdCard("430");
System.out.println("user1.equals(user2)="+user1.equals(user2));
Set set =newHashSet();
set.add(user1);
set.add(user2);
Map map=newHashMap();
map.put(user1,"user1");
map.put(user2,"user2");
System.out.println("set 長度"+set.size());
System.out.println("map 長度"+map.keySet().size());;
System.out.println("user1的hashcode"+user1.hashCode());
System.out.println("user2的hashcode"+user2.hashCode());
打印結果:
4、補充HashMap知識
hashMap組成結構:hashMap是由數組和鏈表組成;
hashMap的存儲:一個對象存儲到hashMap中的位置是由其key 的hashcode值決定的;查hashMap查找key: 找key的時候hashMap會先根據key值的hashcode經過取余算法定位其所在數組的位置,再根據key的equals方法匹配相同key值獲取對應相應的對象;
案例:
(1)hashmap存儲
存值規則:把Key的hashCode 與HashMap的容量 取余得出該Key存儲在數組所在位置的下標(源碼定位Key存儲在數組的哪個位置是以hashCode & (HashMap容量-1)算法得出)這里為方便理解使用此方式;
//為了演示方便定義一個容量大小為3的hashMap(其默認為16)
HashMap map=newHashMap(3);
map.put("a",1);? ? 得到key 為“a” 的hashcode 值為97然后根據 該值和hashMap 容量取余97%3得到存儲位到數組下標為1;
map.put("b",2);? ? 得到key 為“b” 的hashcode 值為98,98%3到存儲位到數組下標為2;
map.put("c",3);? ? 得到key 為“c” 的hashcode 值為99,99%3到存儲位到數組下標為0;
map.put("d",4);? ? 得到key 為“d” 的hashcode 值為100,100%3到存儲位到數組下標為1;
map.put("e",5);? ? 得到key 為“e” 的hashcode 值為101,101%3到存儲位到數組下標為2;
map.put("f",6);? ? 得到key 為“f” 的hashcode 值為102,102%3到存儲位到數組下標為0;
(2)hashmap的查找key
得到key在數組中的位置:根據上圖,當我們獲取key 為“a”的對象時,那么我們首先獲得 key的hashcode97%3得到存儲位到數組下標為1;
匹配得到對應key值對象:得到數組下表為1的數據“a”和“c”對象, 然后再根據 key.equals()來匹配獲取對應key的數據對象;
hashcode 對于HashMapde:如果沒有hashcode 就意味著HashMap存儲的時候是沒有規律可尋的,那么每當我們map.get()方法的時候,就要把map里面的對象一一拿出來進行equals匹配,這樣效率是不是會超級慢;
5、hashcode方法文檔說明
在equals方法沒被修改的前提下,多次調用同一對象的hashcode方法返回的值必須是相同的整數;
如果兩個對象互相equals,那么這兩個對象的hashcode值必須相等;
為不同對象生成不同的hashcode可以提升哈希表的性能;