文章已收錄Github精選,歡迎Star: https://github.com/yehongzhi/learningSummary
寫在前面
其實很早我就注意到阿裡巴巴Java開發規範有一句話:
隻要重寫 equals,就必須重寫 hashCode
。

我想很多人都會問為什麼,所謂
知其然知其是以然
,對待知識不單止知道結論還得知道原因。
hashCode方法
hashCode()方法的作用是擷取哈希碼,傳回的是一個int整數
學過資料結構的都知道,哈希碼的作用是确定對象在哈希表的索引下标。比如HashSet和HashMap就是使用了hashCode方法确定索引下标。如果兩個對象傳回的hashCode相同,就被稱為“哈希沖突”。
equals方法
equals()方法的作用很簡單,就是判斷兩個對象是否相等,equals()方法是定義在Object類中,而所有的類的父類都是Object,是以如果不重寫equals方法則會調用Object類的equals方法。
Object類的equals方法是用“==”号進行比較,在很多時候,因為==号比較的是兩個對象的記憶體位址而不是實際的值,是以不是很符合業務要求。是以很多時候我們需要重寫equals方法,去比較對象中每一個成員變量的值是否相等。
問題來了
重寫equals()方法就可以比較兩個對象是否相等,為什麼還要重寫hashcode()方法呢?
因為HashSet、HashMap底層在添加元素時,會先判斷對象的hashCode是否相等,如果hashCode相等才會用equals()方法比較是否相等。換句話說,HashSet和HashMap在判斷兩個元素是否相等時,會先判斷hashCode,如果兩個對象的hashCode不同則必定不相等。
下面我們做一個試驗,有一個User類,隻重寫equals()方法,然後放到Set集合中去重。
public class User {
private String id;
private String name;
private Integer age;
public User(String id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(name, user.name) &&
Objects.equals(age, user.age);
}
//getter、setter、toString方法
}
然後我們循環建立10個成員變量的值都是一樣的User對象,最後放到Set集合中去重。
public static void main(String[] args) {
List<User> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
User user = new User("1", "張三", 18);
list.add(user);
}
Set<User> set = new HashSet<>(list);
for (User user : set) {
System.out.println(user);
}
List<User> users = list.stream().distinct().collect(Collectors.toList());
System.out.println(users);
}
按道理我們預期會去重,隻剩下一個“張三”的user,但實際上因為沒有重寫hashCode方法,是以沒有去重。
接着我們在User類裡面重寫一些hashCode方法再試試,其他不變。
public class User {
//其他不變
//重寫hashCode方法
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
}
再運作,結果正确。
究其原因在于HashSet會先判斷hashCode是否相等,如果hashCode不相等就直接認為兩個對象不相等,不會再用equals()比較了。我們不妨看看重寫hashCode方法和不重寫hashCode方法的哈希碼。
這是不重寫hashCode方法的情況,每個user對象的哈希碼都不一樣,是以HashSet會認為都不相等。
這是重寫hashCode方法的情況,因為是用對象所有的成員變量的值計算出的哈希碼,是以隻要兩個對象的成員變量都是相等的,則生成的哈希碼是相同的。
那麼有些人看到這裡,就會問,如果兩個對象傳回的哈希碼都是一樣的話,是不是就一定相等?
答案是不一定的,因為HashSet、HashMap判斷哈希碼相等後還會再用equals()方法判斷。
總而言之:
- 哈希碼不相等,則兩個對象一定不相同。
- 哈希碼相等,兩個對象不一定相同。
- 兩個對象相同,則哈希碼和值都一定相等。
總結
是以回到開頭講的那句,
隻要重寫 equals,就必須重寫 hashCode
,這是一個很重要的細節,如果不注意的話,很容易發生業務上的錯誤。
特别是有時候我們明明用了HashSet,distinct()去重,但是就是不生效,這時應該回頭看看重寫了equals()和hashCode()方法了嗎?
那麼這篇文章就寫到這裡了,感謝大家的閱讀。
覺得有用就點個贊吧,你的點贊是我創作的最大動力~
我是一個努力讓大家記住的程式員。我們下期再見!!!
能力有限,如果有什麼錯誤或者不當之處,請大家批評指正,一起學習交流!