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

【Java 每日一題】20161229:List.contains 與 null 元素的 equals 陷阱

     編輯:Java 綜合教學

題目程式碼

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

public class Ques1229 {
    public static void main(String[] args){
        Student stu01 = new Student("001");
        Student stu02 = new Student(null);
        List<Student> students = new ArrayList<>();
        students.add(stu01);
        students.add(stu02);
        System.out.println("students是否包含001學號:" + students.contains(stu01));
        System.out.println("students是否包含null:" + 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;
    }
}

輸出結果

students是否包含001學號:true
Exception in thread "main" java.lang.NullPointerException
    at Student.equals(Ques1229.java:33)
    at java.util.ArrayList.indexOf(ArrayList.java:318)
    at java.util.ArrayList.contains(ArrayList.java:311)
    at Ques1229.main(Ques1229.java:23)

深度解析

為何第一個 true?

  • students.contains(stu01) 內部呼叫 indexOf 遍歷比較
  • 比較 stu01.equals(stu01) → stuId="001" 非 null,正常回傳 true

為何第二個拋 NPE?

  • students.contains(stu02) 遍歷比較:
  • 先與 stu01 比較:stu02.equals(stu01)
  • 進入 equals,stuId == null,執行 null.equalsIgnoreCase(...) → NPE

修正方案

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (!(obj instanceof Student)) return false;
    Student other = (Student) obj;
    // 雙向 null 安全比較
    if (stuId == null && other.stuId == null) return true;
    if (stuId == null || other.stuId == null) return false;
    return stuId.equalsIgnoreCase(other.stuId.trim());
}

或使用 Objects.equals(stuId, other.stuId) (Java 7+)。

核心教訓

  1. equals 必須處理自欄位與他欄位皆為 null 的情況
  2. 集合含 null 元素時,contains(null) 會直接比較 element == null,不呼叫 equals;但 contains(nonNullRefToNullField) 仍會呼叫 equals
  3. 建議使用 Lombok @EqualsAndHashCode 或 IDE 自動生成,減少手寫錯誤

延伸思考

若 stu02 為 new Student("") 空字串,結果如何?請自行驗證 trim() 後空字串比較行為。

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