當類別具有「邏輯相等」語義(而非單純的物件身份比較)時。典型場景:值物件、DTO、實體類、集合元素、Map Key。
if (!(o instanceof MyClass)) return false;| 欄位類型 | 比較方式 |
|---|---|
| 基本型別(非 float/double) | == |
| 物件引用 | 遞歸呼叫 equals() |
| float | Float.floatToIntBits(f1) == Float.floatToIntBits(f2) |
| double | Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2) |
| 陣列 | Arrays.equals(arr1, arr2) |
Java 規範:a.equals(b) == true ⇒ a.hashCode() == b.hashCode()。若僅重寫 equals(),物件放入 HashMap/HashSet 會導致「找不到已放入的元素」等嚴重 Bug。
public int hashCode() {
int result = 17; // 非零質數種子
result = 31 * result + field1Hash; // 31 為質數,乘法散列
result = 31 * result + field2Hash;
// ... 所有參與 equals 比較的欄位
return result;
}
各型別哈希計算對照表:
| 型別 | 計算方式 |
|---|---|
| boolean | (f ? 0 : 1) |
| byte/char/short/int | (int) f |
| long | (int) (f ^ (f >>> 32)) |
| float | Float.floatToIntBits(f) |
| double | Double.doubleToLongBits(d) 再按 long 處理 |
| 物件引用 | f == null ? 0 : f.hashCode() |
| 陣列 | 逐元素遞歸 hashCode() 後合併 |
public class Unit {
private short ashort;
private char achar;
private byte abyte;
private boolean abool;
private long along;
private float afloat;
private double adouble;
private Unit aObject;
private int[] ints;
private Unit[] units;
@Override
public boolean equals(Object o) {
if (!(o instanceof Unit)) return false;
Unit u = (Unit) o;
return u.ashort == ashort
&& u.achar == achar
&& u.abyte == abyte
&& u.abool == abool
&& u.along == along
&& Float.floatToIntBits(u.afloat) == Float.floatToIntBits(afloat)
&& Double.doubleToLongBits(u.adouble) == Double.doubleToLongBits(adouble)
&& (aObject == null ? u.aObject == null : aObject.equals(u.aObject))
&& Arrays.equals(ints, u.ints)
&& Arrays.equals(units, u.units);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (int) ashort;
result = 31 * result + (int) achar;
result = 31 * result + (int) abyte;
result = 31 * result + (abool ? 0 : 1);
result = 31 * result + (int) (along ^ (along >>> 32));
result = 31 * result + Float.floatToIntBits(afloat);
long tmp = Double.doubleToLongBits(adouble);
result = 31 * result + (int) (tmp ^ (tmp >>> 32));
result = 31 * result + (aObject == null ? 0 : aObject.hashCode());
result = 31 * result + Arrays.hashCode(ints);
result = 31 * result + Arrays.hashCode(units); // Java 7+ Arrays.hashCode 支援物件陣列
return result;
}
}
Alt+Insert → equals() and hashCode()、Eclipse Source → Generate hashCode() and equals()。@EqualsAndHashCode 一行搞定,支援 callSuper、onlyExplicitlyIncluded 等進階配置。EqualsVerifier 庫自動驗證契約合規性。equals() 與 hashCode() 是一對「雙胞胎」,缺一不可。遵循 Effective Java 經典演算法,善用 IDE/Lombok 工具,配合 EqualsVerifier 測試,即可寫出健壯、高效的值物件類別。