web-dev-qa-db-ja.com

List.contains()は失敗しますが、.equals()は機能します

ArrayListオブジェクトのTestがあり、等価チェックとして文字列を使用します。 List.contains()を使用して、リストに特定の文字列を使用するオブジェクトが含まれているかどうかを確認できるようにします。

単に:

Test a = new Test("a");
a.equals("a"); // True

List<Test> test = new ArrayList<Test>();
test.add(a);
test.contains("a"); // False!

等しいおよびハッシュ関数:

@Override
public boolean equals(Object o) {
    if (o == null) return false;
    if (o == this) return true;
    if (!(o instanceof Test)) {
        return (o instanceof String) && (name.equals(o));
    }
    Test t = (Test)o;
    return name.equals(t.GetName());
}

@Override
public int hashCode() {
    return name.hashCode();
}

containsがカスタムクラスで機能することを確認するために、equalsをオーバーライドする必要があることを読みました。したがって、equalsがtrueを返す一方で、containsがfalseを返すのは非常に奇妙です。

この作業を行うにはどうすればよいですか?

完全なコード

32
idlackage

Testequalsは、Stringを渡したときにtrueを返す可能性があるからといって、Stringインスタンスを渡したときにequalsTestがtrueを返すとは限りません。実際、Stringequalsは、渡されたインスタンスが別のtrueである場合にのみStringを返すことができます。

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) { // the passed instance must be a String
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

ArrayListcontainsは、検索されたインスタンスのindexOfメソッド(この例ではequals "a")を使用するStringを呼び出し、Listの要素タイプ(これはTestです)を使用します。

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i])) // o in your case is a String while
                                          // elementData[i] is a Test
                                          // so String's equals returns false
                return i;
    }
    return -1;
}
41
Eran

問題は、List<E>.contains(object o)がtrueを返すように文書化されていることです。

このリストに(o == null?e == null:o.equals(e))などの要素eが少なくとも1つ含まれている場合に限ります。

https://docs.Oracle.com/javase/8/docs/api/Java/util/List.html#contains-Java.lang.Object- から)

テストが機能するために必要なe.equals(o)としてテストを実行しないことに注意してください。 equalsメソッドは可換に機能しません(Java docs)の用語を使用して「対称的に」)。

Javaドキュメントでは、クラスのequals()メソッドは次のルールに従う必要があります。

Equalsメソッドは、null以外のオブジェクト参照に等価関係を実装します。

  • 再帰的です。null以外の参照値xの場合、x.equals(x)はtrueを返す必要があります。
  • それは対称です:null以外の参照値xおよびyに対して、x.equals(y)y.equals(x)はtrueを返します。
  • 推移的です。非ヌルの参照値xy、およびzについて、x.equals(y)がtrueを返し、y.equals(z)が返される場合trueの場合、x.equals(z)はtrueを返します。
  • 一貫性があります。null以外の参照値xおよびyの場合、x.equals(y)の複数の呼び出しは一貫してtrueを返すか、一貫してfalseを返します。オブジェクトが変更されます。
  • Null以外の参照値xの場合、x.equals(null)はfalseを返す必要があります。
18
Michael Burr

書くなら

test.contains(new Test("a")); 

その後、必ずtrueを返します。テストのリストで文字列オブジェクトを確認しています。

5
Kunal Surana