首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Java HashSet remove 失效:equals/hashCode 契約違反導致對象無法刪除

     編輯:程式設計疑難解答

問題代碼

class Name {
    String firstname, secondname;
    // 構造器、getter、toString...

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Name) return true; // 錯誤:任意 Name 都相等
        return super.equals(obj);
    }

    @Override
    public int hashcode() { // 錯誤:方法名小寫,未重寫 hashCode()
        return firstname.hashCode();
    }
}

// 測試
Set<Object> c = new HashSet<>();
c.add(new Name("uzi", "omg"));
System.out.println(c.remove(new Name("uzi", "omg"))); // 輸出 false,對象未刪除

核心錯誤:兩處致命

  1. equals 邏輯錯誤:return true 導致**所有 Name 實例互相相等**,違反「自反性、對稱性、傳遞性」。
  2. hashCode 未重寫:方法名 hashcode() 小寫,**未覆寫 Object.hashCode()**,默認返回對象地址哈希。
    **契約要求**:a.equals(b) == true ⇒ a.hashCode() == b.hashCode()。此處 equals 恒真,但 hashCode 不同 → **HashSet 無法定位桶位**,remove 失敗。

正確實現(IDE 自動生成)

class Name {
    private String firstname, secondname;
    // 構造器、getter...

    @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(secondname, name.secondname);
    }

    @Override
    public int hashCode() { // ★ 大寫 C
        return Objects.hash(firstname, secondname);
    }
}

為什麼必須同時重寫?

HashSet/HashMap 存儲邏輯:

  1. 計算 key.hashCode() 確定桶位索引。
  2. 在桶位鏈表/紅黑樹中遍歷,調用 equals() 比較。
  3. 若 hashCode() 不同,**根本進不了同一桶**,equals() 永遠不會被調用。

調試驗證

Name n1 = new Name("uzi", "omg");
Name n2 = new Name("uzi", "omg");
System.out.println(n1.equals(n2));   // true
System.out.println(n1.hashCode());   // 如 123456
System.out.println(n2.hashCode());   // 必須也是 123456
System.out.println(n1 == n2);        // false (不同對象)

最佳實踐

  1. 永遠用 IDE 生成:Eclipse Source → Generate hashCode() and equals(),IDEA Alt+Insert → equals() and hashCode()。
  2. 只用不可變字段**:參與 equals/hashCode 的字段不可修改,否則放入集合後修改導致找不到。
  3. 記住契約口訣**:「等價必同碼,同碼不必等價」。

參考資源

Java equals 和 hashCode 契約深度解析

總結

HashSet remove 返回 false,**99% 是 equals/hashCode 契約違反**。檢查:1. hashCode 方法名大寫 C 2. 邏輯一致性 3. 用不可變字段 4. 用 IDE 自動生成。修正後 c.remove(new Name("uzi","omg")) 返回 true,集合正常維護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved