Java ArrayList包含的方法不能像我预期的那样工作吗?
ArrayList在其Java ArrayList包含的方法不能像我预期的那样工作吗?,java,arraylist,Java,Arraylist,ArrayList在其contains方法中使用equals(),查看提供对象是否等于列表中的任何项,如文档所示: 如果此列表包含指定的元素,则返回true。更多 形式上,当且仅当此列表至少包含一个 元素e使得(o==null?e==null:o.equals(e))。看 我有这门课 class Foo { private String value; public Foo(String value) { this.value = value; } @Override
contains
方法中使用equals()
,查看提供对象是否等于列表中的任何项,如文档所示:
如果此列表包含指定的元素,则返回true。更多
形式上,当且仅当此列表至少包含一个
元素e使得(o==null?e==null:o.equals(e))。看
我有这门课
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?!
List List=new ArrayList();
Foo item1=新Foo(“item1”);
Foo item2=新Foo(“item2”);
增加(第1项);
增加(第2项);
System.out.println(item1.equals(“item1”)//返回真值
System.out.println(list.contains(“item1”)//错!!为什么?
但是包含方法返回false
,而item1.equals(“item1”)
返回true
为什么contains
在为提供的对象使用equals
方法时返回false您的equals方法没有考虑对象的实例,因此实现错误
您有一个foo列表,您想知道该列表是否包含字符串
这:
不一样
System.out.println(list.contains(new Foo("item1")));
因为
new Foo("item1") never ever will return true on equals to the string "item1"
编辑:
ArrayList中的包含实现为
@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;
}
意味着
String.equals(Foo)
这和你做的不一样:
Foo.equals(String)
你打破了对称的规则,这就是它不起作用的原因 问题在于您的equals覆盖:您正在比较o.toString()和this.value
要使其正常工作,请选择以下选项:
重写Foo类上的toString
方法以返回值
将最后一部分更改为this.value.equals(o.getValue())
//必须创建getter
查看包含的文档时,将对字符串“item1”使用equals方法,而不是对item1使用equals方法。e、 g
"item1".equals(item1)
而不是
item1.equals("item1")
您可以使用list.contains(新的Foo(“item1”)
。您的equals()
实现违反了对称原则:
它是对称的:对于任何非空参考值x和y,
x、 当且仅当y.equals(x)返回时,equals(y)应返回true
对
item1.equals(“item1”)
返回true
当
“item1”.equals(item1)
返回false
因此,您不应该期望Collection
方法,例如contains()
以一致的方式工作
作为一般规则,equals()
重写不应尝试与其他类进行互操作,而应仅与基础类的实例进行互操作。
在您的情况下,它仅适用于传递给方法的String
参数。
您应该使其适用于Foo
instances参数:
@Override
public boolean equals(Object o) {
if (!(o instanceof Foo){
return false;
}
Foo other = (Foo) o;
return Objects.equals(other.value, this.value);
}
你的问题是你的等式不是对称的Foo==String
并不意味着String==Foo
如果查看的实现,您会发现它调用了objectToFind.equals(objectInList)
,这可能与您的预期相反:
o.equals(elementData[i])
在您的例子中,这是String.equals(Foo)
。因为String.equals
对于任何非字符串的内容都将返回false,ArrayList.contains
将返回false。您的equals实现显然是不正确的
您应该阅读Joshua Bloch的“有效Java”,以了解如何正确重写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();
}
}
当item1.equals(“item1”)
为true时,“item1.equals(item1)
将为false。使用非对称的equals关系会引起很多混乱
通常,您希望equals
仅在您控制的类中为真(通常仅为您正在比较的确切类),以便确保关系是对称的。(为了避免进一步的混淆,无论何时定义equals
,都要定义hashCode
)我希望返回true,因为我重写了Foo
new Foo(“item1”)中的equals方法,它永远不会在等于字符串“item1”时返回true,而字符串“item1”则不是true。您可以编写一个equals()实现,以确保@ΦXnew Foo(“item1”)
永远不会在等于字符串“item1”时返回true为什么不呢?如果我重写Foo
类中的equals
方法并使其始终返回true?您的意思可能与此相反:字符串“item1”永远不会在等于new Foo(“item1”)时返回true,感谢您的澄清o
是传递到contains
中的参数(在本例中为字符串
),e
是列表的元素(您的类Foo
)-o.equals(e)
与e.equals(o)
在列表中查找实际的对象类型,而不是字符串。也就是说,在一个FOO列表中找到一个FOO,而不是在一个FOO列表中找到一个字符串。为什么要在equals()方法中使用“@override”?您的类没有扩展任何其他类,我认为您的equal()与您要声明的任何其他方法类似。@ZiMtyth否。他正在从对象
类重写它。强烈建议您添加此注释,以确保具有良好的signature@nathan,谢谢你的澄清:)谢谢你的帮助,我确实试过了,但现在两者都返回了false
。否决票不是来自me@Michael否。例如,TO的equals
方法不是自反的(不确定这是否是术语):a.equals(b)
并不总是与b.equals(a)相同代码>。此默认值是它不可用的原因之一work@Nathan对称的。这里有一个术语列表。自反性将是x.equals(x)=true
非常绝对的一个术语。约书亚·布洛赫(Joshua Bloch)准确地阐述了如何正确地完成这项工作。如果你不听从他的建议,那就错了。简单。你好
@Override
public boolean equals(Object o) {
if (!(o instanceof Foo){
return false;
}
Foo other = (Foo) o;
return Objects.equals(other.value, this.value);
}
o.equals(elementData[i])
/**
* 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();
}
}