3

これは、インスタンスの 1 つがメンバーにある値を指定して、リスト内のオブジェクトのインスタンスを見つける必要がある場合に equals メソッドを実装する方法についての質問です。

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);
}
4

5 に答える 5

7

2 番目の方法で行うと、等価性の対称性が損なわれるため、危険です。

Java は、 の実装equals()が再帰的、対称的、および推移的であることを期待しています。2 番目の実装は対称性を破ります。ID を表す と比較Userすると が得られますが、文字列をユーザーと比較すると が得られます。Stringtruefalse

于 2012-04-04T19:15:54.557 に答える
6

数学的に等しくないものに対して equals をオーバーライドしないでください。

あなたはそれが良い考えだと思うかもしれません

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

しかし、めったにありません。Stringsと「等しい」場合にコードを観察するすべての等号が混乱することを望みますUsersか?

ルックアップメソッドが必要な場合は、それを記述します

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 の実装を混乱させないことに注意してください。そのため、何らかの形で String が動作を妨害するかどうかを心配することなく、Setsof UsersListsofなどをすべて安全に使用できるようになりました。Users

によってインデックス付けされた を維持するMapのに適した場所が見つからない場合は、いつでもより遅い解決策を使用できます。UsersidIterator

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;
}
于 2012-04-04T19:17:08.823 に答える
2

まず、2 番目の実装は 1 番目の実装と機能的に同等です。これは、 のインスタンスが のインスタンスでStringはなくUser、最初のreturnステートメントが a のチェックが行われる前にそれを短絡するため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);
}

ここで、実際の問題に進みます。

-to- comparisonequalsを返すtrueを実装することは、対称であることが期待されるため( if and only if )、悪い習慣です。また、 aが a と等しくなることはあり得ないため、 a が a と等しくなることはありません。UserStringequalsa.equals(b)b.equals(a)StringUserUserString

于 2012-04-04T19:16:35.877 に答える
1

Object.equals()をオーバーライドする場合は、Object.hashCode()をオーバーライドすることを忘れないでください。

等しいオブジェクトは等しいハッシュコードを持っている必要があり、コントラクトに従わない場合、コレクションで問題が発生する可能性があります。

于 2012-04-04T19:41:52.930 に答える
0

2番目の選択は非常に悪いです。これは、ユーザーが a 以外のものを渡すことを許可していることを意味しますUser(User.equals()つまり、実際には文字列をユーザーと比較しようとしていないときです。したがって、本質的には、本来行うべきことの契約に違反しObject.equals()います。equals

また、どちらの回答もヌルチェックを処理しません。

于 2012-04-04T19:19:17.630 に答える