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

时间:2016-02-16 06:47:45

标签: java list equals contains

我有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适用于自定义类,它需要覆盖equals。因此,当equals返回true时,contains返回false,这对我来说非常奇怪。

我该如何做到这一点?

Full code

4 个答案:

答案 0 :(得分:41)

只是因为当您向其传递字符串时,Test的{​​{1}}可能会返回true,这并不意味着equals的{​​{1}}将永远返回true您将String实例传递给它。事实上,equals的{​​{1}}只能在传递给它的实例是另一个Test时返回String

equals

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; } ,它使用搜索实例的ArrayList方法(在您的示例中为contains“a”),而非indexOf的元素类型(在您的情况下为equals):

String

答案 1 :(得分:27)

equals()应始终为commutative,即a.equals(b)b.equals(a)应始终返回相同的值。或对称,因为equals()的javadoc称之为:

  

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

     
      
  • reflexive :对于任何非空参考值xx.equals(x)应返回true
  •   
  • 对称:对于任何非空引用值xy,如果和x.equals(y) true应返回y.equals(x)仅当true返回x
  • 时   
  • 传递:对于任何非空引用值yzx.equals(y),如果true返回{{1} }和y.equals(z)返回true,然后x.equals(z)应该返回true
  •   
  • 一致:对于任何非空引用值xyx.equals(y)的多次调用始终返回true或始终如一返回false,前提是没有修改对象equals比较中使用的信息。
  •   
  • 对于任何非空引用值xx.equals(null)应返回false
  •   

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

答案 2 :(得分: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上实现等价关系   对象引用:

     
      
  • 它是自发的:对于任何非空引用值xx.equals(x)应该返回true。
  •   
  • 它是对称的:对于任何非空引用值xy,当{且仅x.equals(y)返回true时,y.equals(x)应返回true。 /强>
  •   
  • 它是传递性的:对于任何非空引用值xyz,如果x.equals(y)返回true并且y.equals(z)返回true,然后x.equals(z)应该返回true。
  •   
  • 一致:对于任何非空引用值xyx.equals(y)的多次调用始终返回true或始终返回false,前提是没有在等于比较时使用的信息对象被修改。
  •   
  • 对于任何非空引用值xx.equals(null)应返回false。
  •   

答案 3 :(得分:5)

如果你写

test.contains(new Test("a")); 
然后它肯定会回归真实。您正在检查Test列表中的字符串对象。