List.contains() 失败,而 .equals() 工作
Posted
技术标签:
【中文标题】List.contains() 失败,而 .equals() 工作【英文标题】:List.contains() fails while .equals() works 【发布时间】:2016-05-27 07:43:34 【问题描述】:我有一个 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。
我怎样才能做到这一点?
Full code
【问题讨论】:
一个类只能用同一个类测试equals
。
您对equals
的作用的想法是错误的,您应该将Test
与Test
的另一个实例进行比较
只有反过来才行,因为"a".equals(new Test("a")) == false
另外,"a".equals(new Test("a")) != new Test("a").equals("a")
是个问题。
@bayou.io Object.equals
的合约默认需要它,因为非空equal
ity 的交换属性。
【参考方案1】:
equals()
应始终为 commutative,即 a.equals(b)
和 b.equals(a)
应始终返回相同的值。或者 对称,正如 equals()
的 javadoc 所称:
它是自反的:对于任何非空引用值
equals
方法在非空对象引用上实现等价关系:x
,x.equals(x)
应该返回true
。 它是对称的:对于任何非空引用值x
和y
,当且仅当y.equals(x)
返回@987654336 时x.equals(y)
应该返回true
@。 它是可传递的:对于任何非空引用值x
、y
和z
,如果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
。
不幸的是,即使是 Java 运行时库也弄错了这一点。 Date.equals(Timestamp)
将比较毫秒值,忽略 Timestamp
中存在的纳秒,而 Timestamp.equals(Date)
返回 false
。
【讨论】:
+1 保持非对称equals
会使代码变得不必要地脆弱且难以维护,因为它依赖于完成循环测试的方式。
JDK 中有更大的不对称性 - Map 接口。如果您想知道为什么 Map 有 get(Object key) 而不是 get(K key) - 正是为了允许传递碰巧破坏了对 K 实例的相等性的随机类。在 Java 的一个版本中,Sun 更改了此顺序(因此将 map 中的值与传递的键进行比较,而不是其他方式),它破坏了很多应用程序,他们不得不依靠它。【参考方案2】:
如果你写
test.contains(new Test("a"));
那么它肯定会返回true。您正在检查测试列表中的字符串对象。
【讨论】:
所以我们需要使用contains
调用创建一个新对象?【参考方案3】:
问题是 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
,当且仅当y.equals(x)
返回 true 时,x.equals(y)
应该返回 true。 它是可传递的:对于任何非空引用值x
、y
和z
,如果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。
【讨论】:
【参考方案4】:仅仅因为您的Test
的equals
可能会在您将字符串传递给它时返回true,但这并不意味着String
的equals
在您将Test
实例传递给它时将永远返回true它。其实String
的equals
只有在传递给它的实例是另一个String
时才能返回true
:
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
调用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;
【讨论】:
new Test("a").equals("a")
返回 true,而 "a".equals(new Test("a"))
返回 false,很好解释!谢谢伊兰!
啊,这很有意义。所以基本上解决这个问题的唯一方法(也是解决这个问题的唯一正确方法)是写test.contains(new Test("a"))
?
我认为我们有o.equals(elementData[i]
而不是它的倒数的原因是因为我们有o != null
的保证,而object[]
没有这样的保证?
@BenKnoble o
由于indexOf
的具体实现,保证不为空。另一种可行的实现是有一个循环来检查每个elementData[i]
是否为空,如果elementData[i],equals(o)
不为空,则运行elementData[i],equals(o)
。当o != null
时,该实现将需要更多的空检查,这可能是它未被选中的原因。以上是关于List.contains() 失败,而 .equals() 工作的主要内容,如果未能解决你的问题,请参考以下文章
令人惊讶的性能差异:List.Contains、Sorted List.ContainsKey、DataRowCollection.Contains、Data Table.Select、DataTab