ArrayList在其equals()
方法中使用contains
来查看提供对象是否等于列表中的任何项目,如文档所示:
如果此列表包含指定的元素,则返回true。更多 正式地,当且仅当此列表包含至少一个时才返回true 元素e使得(o == null?e == null:o.equals(e))。见this
我有这个班级
class Foo
{
private String value;
public Foo(String value)
{
this.value = value;
}
@Override
public boolean equals(Object o)
{
return o == null ? this.value == null : o.toString().equals(this.value);
}
}
我想使用contains
方法检查项目是否像这样存在
List<Foo> list = new ArrayList<Foo>();
Foo item1 = new Foo("item1");
Foo item2 = new Foo("item2");
list.add(item1);
list.add(item2);
System.out.println(item1.equals("item1")); //return true
System.out.println(list.contains("item1")); //false !! why?!
但contains
方法返回false
,而item1.equals("item1")
返回true
。
为contains
使用equals
方法提供对象
答案 0 :(得分:6)
您的equals()
实施违反了对称原则:
它是对称的:对于任何非空参考值x和y, 当且仅当y.equals(x)返回时,x.equals(y)才应返回true 真。
item1.equals("item1")
返回true
,而
"item1".equals(item1)
返回false
。
因此,您不应期望Collection
方法(例如contains()
)以一致的方式运作。
作为一般规则,equals()
覆盖不应该尝试与其他类互操作,而只应与基础类的实例互操作。
在您的情况下,它仅适用于传递给方法的String
参数
您应该使其适用于Foo
实例参数:
@Override
public boolean equals(Object o) {
if (!(o instanceof Foo){
return false;
}
Foo other = (Foo) o;
return Objects.equals(other.value, this.value);
}
答案 1 :(得分:5)
查看文档here包含将在字符串&#34; item1&#34;上使用equals方法。而不是item1上的equals方法。 e.g。
"item1".equals(item1)
而不是
item1.equals("item1")
您可以改用list.contains(new Foo("item1"))
。
答案 2 :(得分:5)
你的问题是你的平等不对称。 Foo == String
并不意味着String == Foo
。
如果您查看ArrayList.contains
的实施,您会看到它调用objectToFind.equals(objectInList)
,这可能与您的预期相反:
o.equals(elementData[i])
所以在你的情况下是String.equals(Foo)
。因为String.equals
将为非String的任何内容返回false,ArrayList.contains
将返回false。
答案 3 :(得分:4)
item1.equals("item1")
为真,"item1".equals(item1)
将为false。具有非对称等于关系会导致很多混淆。
通常,您希望equals
仅在您控制的类中成为true(通常只是您要比较的确切类),以便您可以确保关系是对称的。 (为了避免进一步混淆,只要定义hashCode
,就要定义equals
。)
答案 4 :(得分:2)
你的Equals方法没有考虑Object的实例,所以错误地实现了
你有一个Foos列表,你想知道列表是否包含STRING
这样:
OKHttp client = ....
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.interceptors().add(interceptor);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("url")
.client(client) // add custom OkHttp client
与
不同System.out.println(list.contains("item1"));
因为
System.out.println(list.contains(new Foo("item1")));
编辑:
包含在ArrayList中实现为
new Foo("item1") never ever will return true on equals to the string "item1"
所以这部分是重要的一部分
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
你可以看到
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
表示
o.equals(a[i])
与你所做的不同:
String.equals(Foo)
你违反了对称的规则,这就是为什么不起作用的原因!
答案 5 :(得分:2)
您的平等实施显然不正确。
你应该阅读Joshua Bloch的“Effective Java”的chapter 3来学习如何正确地覆盖equals和hashcode。
这会更好用:
/**
* Demo equals override
* User: mduffy
* Date: 8/10/2017
* Time: 10:45 AM
* @link https://stackoverflow.com/questions/45616794/arraylist-contains-method-not-work-as-i-would-except
*/
public class Foo {
private final String value;
public Foo(String value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Foo)) return false;
Foo foo = (Foo) o;
return value != null ? value.equals(foo.value) : foo.value == null;
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Foo{");
sb.append("value='").append(value).append('\'');
sb.append('}');
return sb.toString();
}
}
答案 6 :(得分:1)
问题在于你的等于覆盖:你正在比较o.toString()和this.value。
让它运作这是你的选择:
toString
方法以返回value
this.value.equals(o.getValue())
//必须创建getter