List.contains() 失败,而 .equals() 有效

List.contains() fails while .equals() works

我有一个 ArrayListTest 个对象,它们使用字符串作为等价性检查。我希望能够使用 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 适用于自定义 class,它需要覆盖 equals。因此,对我来说超级奇怪的是,虽然 equals returns 是真的,但 contains returns 是假的。

我怎样才能完成这项工作?

Full code

仅仅因为你的 Testequals 可能 return 在你传递一个字符串给它时为真并不意味着 Stringequals 将永远 return 当您将 Test 实例传递给它时为真。事实上,Stringequals 只能 return 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 方法(您的 String "a"例如),而不是 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;
}

问题是 List<E>.contains(object o) 被记录为 return true:

if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)).

(来自 https://docs.oracle.com/javase/8/docs/api/java/util/List.html#contains-java.lang.Object-

请注意,它不会像 e.equals(o) 那样执行测试,这是您的测试工作所必需的。您的 equals 方法无法交换地工作('symmetrically' 使用 Java 文档中的术语)。

Java 说明 class 的 equals() 方法必须遵循以下规则:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

equals() 应该总是 commutative, i.e. a.equals(b) and b.equals(a) should always return the same value. Or symmetric, as the javadoc of equals() 调用它:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

不幸的是,即使 Java 运行时库也会出错。 Date.equals(Timestamp) 将比较毫秒值,忽略 Timestamp 中存在的纳秒,而 Timestamp.equals(Date) returns false.

如果你写

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

那么它肯定return是真的。您正在检查测试列表中的字符串对象。