Set<Object> set = new HashSet<>();
set.add("hello");
set.add(new Name("lu", "dongdong"));
set.add(new Integer(100));
System.out.println(set.remove(new Integer(100))); // true
System.out.println(set.remove(new Name("lu", "dongdong"))); // false
| 類型 | equals 實現 | hashCode 實現 | new 實例是否相等 |
|---|---|---|---|
Integer (包裝類) |
比較 數值 | 基於數值計算 | 是:new Integer(100).equals(new Integer(100)) == true |
String |
比較 字符序列 | 基於字符計算 | 是 (字面量池/內容相同) |
Name (自定義類,未重寫) |
繼承 Object.equals → 比較引用地址 |
繼承 Object.hashCode → 基於地址 |
否:new Name(...) 產生不同對象,引用不等 |
public boolean remove(Object o) {
int hash = hash(o);
for (Node<E> e = table[hash & (table.length-1)]; e != null; e = e.next) {
if (e.hash == hash && (e.key == o || o.equals(e.key))) { // 關鍵:調用 equals
removeNode(hash, e.key, null, false, true);
return true;
}
}
return false;
}
刪除時會遍歷桶,**調用 o.equals(e.key)** 比較。Integer 重寫了 equals 比較值,Name 未重寫比較引用 → 導致找不到匹配元素。
class Name {
private String firstName, lastName;
public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Name name = (Name) o;
return Objects.equals(firstName, name.firstName) &&
Objects.equals(lastName, name.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
// toString 保持不變
}
重寫後:set.remove(new Name("lu", "dongdong")) 返回 true。
// Lombok
@Data // 含 @EqualsAndHashCode
class Name {
private String firstName, lastName;
}
// Java 16+ Record (隱式 final、equals/hashCode/toString 自動實現)
record Name(String firstName, String lastName) {}
equals/hashCode 比較內容,new 實例可相等。HashSet/HashMap 作為鍵/元素時,**必須重寫 equals/hashCode** 基於業務鍵比較。HashSet.remove、contains、Map.get 都依賴 equals 契約。