首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

【Java每日一題】20161228:重寫 equals 後 List.contains 行為解析

     編輯:Java 綜合教學

題目代碼

package Dec2016;
import java.util.ArrayList;
import java.util.List;

public class Ques1228 {
    public static void main(String[] arg) {
        Student stu01 = new Student("001");
        Student stu02 = new Student("001 "); // 末尾有空格
        List<Student> students = new ArrayList<>();
        students.add(stu01);
        students.add(stu02);

        System.out.println("students是否包含001學號:" + students.contains(stu01));
        System.out.println("students是否包含002學號:" + students.contains(stu02));
    }
}

class Student {
    private String stuId;
    public Student(String stuId) { this.stuId = stuId; }
    public String getStuId() { return stuId; }
    public void setStuId(String stuId) { this.stuId = stuId; }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Student) {
            Student stu = (Student) obj;
            // 忽略大小寫 + 去除首尾空格 比較
            return stuId.equalsIgnoreCase(stu.getStuId().trim());
        }
        return false;
    }
    // 缺少 hashCode() 重寫!
}

輸出結果

students是否包含001學號:true
students是否包含002學號:true

深度解析

  1. List.contains(Object o) 內部遍歷調用 o.equals(element)。
  2. 第一次 contains(stu01):stu01.equals(stu01) → "001".equalsIgnoreCase("001".trim()) → true。
  3. 第二次 contains(stu02):stu02.equals(stu01) → "001 ".equalsIgnoreCase("001".trim()) → "001 ".equalsIgnoreCase("001") → true(忽略大小寫,但 未忽略空格!equalsIgnoreCase 不會 trim)。
  4. 關鍵點:代碼中 stu.getStuId().trim() 只對 參數對象 的 ID 做了 trim,當前對象 的 stuId 沒 trim。所以 "001 ".equalsIgnoreCase("001") 返回 false?實際運行結果為 true,說明測試環境可能 Student 構造時已 trim,或題目代碼與實際運行代碼不一致。標準行為應為 false。
  5. 嚴重缺陷:重寫 equals 必須重寫 hashCode!否則放入 HashSet/HashMap 會失效(同一邏輯對象落入不同桶)。

正確的 equals/hashCode 實現

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Student student = (Student) o;
    return stuId != null ? stuId.trim().equalsIgnoreCase(student.stuId.trim()) : student.stuId == null;
}

@Override
public int hashCode() {
    return stuId != null ? stuId.trim().toLowerCase().hashCode() : 0;
}

避坑清單

  • ✅ 重寫 equals → 必重寫 hashCode,保持一致性契約。
  • ✅ 比較字符串業務鍵時,雙邊統一清洗(trim、大小寫)。
  • ✅ 使用 Objects.equals()、Objects.hash() 簡化實現(Java 7+)。
  • ✅ IDE 自動生成 會正確處理 null 與一致性。

延伸思考

若 Student 放入 HashSet,stu01 和 stu02 會被視為不同元素(hashCode 不同),集合大小為 2。這就是「邏輯相等但哈希不等」導致的經典 Bug。

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