[英]Should hashCode() only use the subset of immutable fields of those used in equals()?
情況
我需要覆蓋equals()
,因為我建議我也使用相同的字段覆蓋hashCode()
方法。 然后,當我看着一個只包含一個對象的集合時,我得到了令人沮喪的結果
set.contains(object)
=> false
而
set.stream().findFirst().get().equals(object)
=> true
我現在明白了,這是因為在將object
添加到set
之后對object
所做的更改再次更改了其hashCode。 然后contains
查找錯誤的鍵並找不到該object
。
我對實施的要求是
equals()
Collections
或Maps
安全地使用這些對象,例如灰燼HashSet
。 這違反了慣例
equals()
和hashCode()
應該使用相同的字段以避免意外(如下所述: https : //stackoverflow.com/a/22827702 )。 題
僅使用equals()
中使用的字段子集計算hashCode()
而不是全部使用是否存在任何危險?
更具體而言,這將意味着: equals()
使用了編號的對象,而字段的hashCode()
只使用了在用於這些領域equals()
和是不可變的 。
我認為這應該沒問題,因為
HashSet
找到。 相關帖子幫助我理解了我的問題,但沒有解決方法: 在Java中覆蓋equals和hashCode時應該考慮哪些問題? 和equals和hashcode的不同字段
合同確實會實現。 合同強制.equal()
對象始終具有相同的.hashCode()
。 相反的情況不一定是真的,我想知道一些人和IDE的痴迷恰好適用於這種做法。 如果所有可能的組合都可以,那么你會發現完美的哈希函數。
順便說一句,IntelliJ在生成hashCode時提供了一個很好的向導,並且通過分別處理這兩個方法並允許區分您的選擇來提供相同的向導。 顯然,相反,又名提供在更多領域hashCode()
和更少的領域中equals()
違反約定。
對於HashSet
和類似的集合/映射,使用hashCode()
只使用equals()
方法中的字段子集是一種有效的解決方案。 當然,您必須考慮哈希碼有效減少地圖中的沖突。
但請注意,如果要使用TreeSet
等有序集合,問題就會出現。 然后你需要一個永遠不會給“不同”對象發生沖突(返回零)的比較器,這意味着該集合只能包含一個碰撞元素。 你的equals()
描述意味着存在多個只在可變字段中不同的對象,然后你輸了:
所以我強烈建議再次考慮你的對象類的平等和可變性的概念。
這對我來說完全有效。 假設你有一個Person
:
final int name; // used in hashcode
int income; // name + income used in equals
name
決定條目的位置(想想HashMap
)或選擇哪個桶。
你把一個Person
作為Key
放在HashMap
:根據hashcode
它會進入某個桶,例如第二個。 您升級income
並在地圖中搜索該Person
。 根據hashcode
它必須在第二個桶中,但根據equals
它不存在:
static class Person {
private final String name;
private int income;
public Person(String name) {
super();
this.name = name;
}
public int getIncome() {
return income;
}
public void setIncome(int income) {
this.income = income;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object other) {
Person right = (Person) other;
return getIncome() == right.getIncome() && getName().equals(right.getName());
}
}
並測試:
HashSet<Person> set = new HashSet<>();
Person bob = new Person("bob");
bob.setIncome(100);
set.add(bob);
Person sameBob = new Person("bob");
sameBob.setIncome(200);
System.out.println(set.contains(sameBob)); // false
您認為缺少的是hashcode
決定一個條目所在的桶(該桶中可能有許多條目),這是第一步,但是equals
決定它是否合適,是一個相等的條目。
您提供的示例完全合法; 但你鏈接的那個是另一種方式 - 它在hashcode
使用更多的字段,因此它是錯誤的。
如果您了解這些細節,那么第一個哈希碼用於了解Where和Entry 可能駐留的位置,並且只有稍后所有這些(來自子集或存儲桶)都試圖通過equal
方式找到 - 您的示例才有意義。
hashCode()
可以使用equals()
使用的字段的子集,盡管它可能會給你一點性能下降。
您的問題似乎是由於修改了對象,同時仍然在集合內部,以改變hashCode()
和/或equals()
。 無論何時將對象添加到HashSet(或作為HashMap中的鍵),都不得隨后修改equals()
和/或hashCode()
使用的該對象的任何字段。 理想情況下, equals()
使用的所有字段都應該是final
。 如果它們不可能,則必須將它們視為最終,同時對象在集合中。
TreeSet / TreeMap也是如此,但適用於compareTo()
使用的字段。
如果您確實需要修改equals()
使用的字段(或者在TreeSet / TreeMap的情況下通過compareTo()
),您必須:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.