当.equals()工作时,List.contains()失败

idl*_*age 33 java contains list equals

我有一个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!
Run Code Online (Sandbox Code Playgroud)

等于和散列函数:

@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();
}
Run Code Online (Sandbox Code Playgroud)

我读到这是为了确保contains自定义类的工作,它需要覆盖equals.因此,当我equals返回true时contains返回false 对我来说是非常奇怪的.

我怎样才能做到这一点?

完整代码

Era*_*ran 41

仅仅因为你Testequals时候,你可以通过返回true一个字符串它并不意味着Stringequals,当你通过一个永远不会返回true,Test例如它.事实上,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;
}
Run Code Online (Sandbox Code Playgroud)

ArrayList使用搜索实例的方法(在您的示例中为"a")的contains调用,而不是元素类型(在您的情况下):indexOfequalsStringListTest

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;
}
Run Code Online (Sandbox Code Playgroud)

  • `new Test("a").equals("a")`返回true,而`"a".equals(new Test("a"))`返回false,很好的解释!谢谢伊兰! (9认同)
  • 啊,现在这很有道理.所以基本上解决这个问题的唯一方法(以及唯一正确的解决方法)是编写`test.contains(new Test("a"))`` (4认同)

And*_*eas 27

equals()应该永远是可交换的,即a.equals(b)b.equals(a)应始终返回相同的值.或者对称,作为equals()调用它的javadoc :

equals方法在非null对象引用上实现等价关系:

  • 它是自反的:对于任何非空引用值x,x.equals(x)应该返回true.
  • 它是对称的:对于任何非空引用值xy,x.equals(y)应该返回true当且仅当y.equals(x)回报true.
  • 它是传递性:对于任何非空的参考值x,y以及z,如果x.equals(y)回报率truey.equals(z)回报率true,那么x.equals(z)应该返回true.
  • 它是一致的:对于任何非空引用值x以及一致返回或一致返回的y多次调用,前提是不修改在对象比较中使用的信息.x.equals(y)truefalseequals
  • 对于任何非空引用值x,x.equals(null)应返回false.

不幸的是,即使Java运行时库也错了.Date.equals(Timestamp)将比较毫秒值,忽略存在的纳秒Timestamp,同时Timestamp.equals(Date)返回false.


Mic*_*urr 18

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

当且仅当此列表包含至少一个元素e时(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方法无法交换(使用Java文档中的术语"对称").

Java文档表示equals()类的方法必须遵循以下规则:

equals方法在非null对象引用上实现等价关系:

  • 它是自反的:对于任何非空引用值x,x.equals(x)应该返回true.
  • 它是对称的:对于任何非空引用值xy,x.equals(y)应真,当且仅当返回y.equals(x)返回true.
  • 它是传递性的:对于任何非空引用值x,yz,如果x.equals(y)返回true并y.equals(z)返回true,x.equals(z)则应返回true.
  • 它是一致的:对于任何非空引用值,x并且y多次调用x.equals(y)始终返回true或始终返回false,前提是不修改在对象的equals比较中使用的信息.
  • 对于任何非空引用值x,x.equals(null)应返回false.


Kun*_*ana 5

如果你写

test.contains(new Test("a")); 
Run Code Online (Sandbox Code Playgroud)

那肯定会回归真实.您正在检查Test列表中的字符串对象.