我有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,这对我来说非常奇怪。
我该如何做到这一点?
答案 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 :对于任何非空参考值
x
,x.equals(x)
应返回true
。- 对称:对于任何非空引用值
时x
和y
,如果和x.equals(y)
true
应返回y.equals(x)
仅当true
返回x
。- 传递:对于任何非空引用值
y
,z
和x.equals(y)
,如果true
返回{{1} }和y.equals(z)
返回true
,然后x.equals(z)
应该返回true
。- 一致:对于任何非空引用值
x
和y
,x.equals(y)
的多次调用始终返回true
或始终如一返回false
,前提是没有修改对象equals
比较中使用的信息。- 对于任何非空引用值
x
,x.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上实现等价关系 对象引用:
- 它是自发的:对于任何非空引用值
x
,x.equals(x)
应该返回true。- 它是对称的:对于任何非空引用值
x
和y
,当{且仅x.equals(y)
返回true时,y.equals(x)
应返回true。 /强>- 它是传递性的:对于任何非空引用值
x
,y
和z
,如果x.equals(y)
返回true并且y.equals(z)
返回true,然后x.equals(z)
应该返回true。- 一致:对于任何非空引用值
x
和y
,x.equals(y)
的多次调用始终返回true或始终返回false,前提是没有在等于比较时使用的信息对象被修改。- 对于任何非空引用值
x
,x.equals(null)
应返回false。
答案 3 :(得分:5)
如果你写
test.contains(new Test("a"));
然后它肯定会回归真实。您正在检查Test列表中的字符串对象。