Java 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

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
  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()实现,以确保@ΦX
    new 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();
        }
    }