簡體   English   中英

首選 Equals() 方法實現

[英]Preferred Equals() Method Implementation

這是一個關於當我需要在列表中找到 object 的實例時如何實現 equals 方法的問題,給定我在其成員中擁有的實例之一的值。

我有一個 object,我在其中實現了 equals:

class User {

    private String id;

    public User(id) {
        this.id = id;
    }

    public boolean equals(Object obj) {
        if (!(obj instanceof User)) {
            return false;
        }
        return ((User)obj).id.equals(this.id);
    }
}

現在,如果我想在列表中找到一些東西,我會這樣做:

public function userExists(String id) {
        List<Users> users = getAllUsers(); 
        return users.contains(new User(id));
}

但也許這可能是更好的實施方式?

class User {

    private String id;

    public boolean equals(Object obj) {
        if (!(obj instanceof User)) {
            return false;
        }
        if (obj instanceof String) {
            return ((String)obj).equals(this.id);
        }
        return ((User)obj).id.equals(this.id);
    }
}

有了這個:

public function userExists(String id) {
    List<Users> users = getAllUsers(); 
    return users.contains(id);
}

以第二種方式進行是危險的,因為它破壞了等式的對稱性

Java 期望equals()的實現是自反的、對稱的和可傳遞的。 第二種實現打破了對稱性:如果將User與表示其 ID 的String進行比較,您將得到true ,但如果將字符串與 user 進行比較,則會得到false

不要為數學上不相等的事物覆蓋等於。

你可能認為這樣做是個好主意

User bob = new User("Bob");
if (bob.equals("Bob")) {
  ...
}

但它很少是。 StringsUsers “相等”時,您是否希望所有 equals 觀察代碼都感到困惑?

如果你想要一個查找方法,寫它

class User {

    private String id;

    public boolean equals(Object obj) {
        if (obj instanceof User) {
            User other = (User)obj;
            if (id.equals(other.id)) {
              return true;
            }
        }
        return false;
    }

    public String getId() {
        return id;
    }

}

然后其他地方的代碼維護“快速查找”表。

Map<String, User> idTable = new HashMap<String, User>();
User bob = new User("Bob");
idTable.put(bob.getId(), bob);

public User findUser(String id) {
  return idTable.get(id);
}

請注意,這不會與 equals 實現混淆,因此現在您可以安全地擁有Sets of UsersLists of Users等,而不必擔心 String 是否會以某種方式破壞工作。

現在如果你找不到一個好的地方來維護一個MapUsers indexed by their id ,你總是可以使用更慢的Iterator解決方案

List<User> users = new List<User>();
users.add(new User("Bob"));
users.add(new User("Steve"));
users.ass(new User("Ann"));

public User findUser(String id) {
  Iterator<User> index = users.iterator();
  while (index.hasNext()) {
    User user = index.next();
    if (id.equals(user.getId())) {
      return user;
    }
  }
  return null;
}

首先,您的第二個實現在功能上等同於第一個,因為String的實例不是User的實例,並且第一個return語句將在檢查String之前將其短路。

我的意思是,

public boolean equals(Object obj) {
    if (!(obj instanceof User)) { // This will execute if obj is a String
        return false;
    }
    if (obj instanceof String) {
        // Never executes, because if obj is a String, we already
        // returned false above
        return ((String)obj).equals(this.id);
    }
    return ((User)obj).id.equals(this.id);
}

因此,對於其余的答案,我將假設其意思是

public boolean equals(Object obj) {
    if ( obj == null ) return false; // Add a null check for good measure.
    if (!(obj instanceof User)) {
        if (obj instanceof String) {
            // Now we're checking for a String only if it isn't a User.
            return ((String)obj).equals(this.id);
        }
        return false;
    }
    return ((User)obj).id.equals(this.id);
}

現在我們來解決實際問題。

實現一個為UserString比較返回trueequals是不好的做法,因為equals應該是對稱的( a.equals(b)當且僅當b.equals(a) )。 並且由於String永遠不能等於User ,所以User永遠不應該等於String

如果您覆蓋 Object.equals(),請不要忘記覆蓋 Object.hashCode()。

相同的對象必須具有相同的 hash 代碼,如果您不遵守合同,您可能會遇到 Collections 的問題。

第二個選擇很糟糕。 這意味着您允許用戶將User以外的其他內容傳遞給User.equals() (即,實際上是Object.equals()當您實際上並沒有嘗試將 String 與 User 進行比較時。所以您實際上違反了equals應該做什么的合同。

此外,您的答案都不處理空值檢查。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM