【问题标题】:.Contains() method not calling Overridden equals method.Contains() 方法不调用 Overridden equals 方法
【发布时间】:2012-03-08 07:14:04
【问题描述】:

我有一个问题,我创建了一个 Foo 对象的 ArrayList,我覆盖了 equals 方法,但我无法让 contains 方法调用 equals 方法。我试过同时覆盖equals和hashcode,但它仍然不起作用。我敢肯定有一个合乎逻辑的解释为什么会这样,但我现在无法自己弄明白,哈哈。我只是想要一种方法来查看列表是否包含指定的 id。

这里有一些代码:

import java.util.ArrayList;
import java.util.List;

public class Foo {

    private String id;


    public static void main(String... args){
        Foo a = new Foo("ID1");
        Foo b = new Foo("ID2");
        Foo c = new Foo("ID3");
        List<Foo> fooList = new ArrayList<Foo>();
        fooList.add(a);
        fooList.add(b);
        fooList.add(c);
        System.out.println(fooList.contains("ID1"));
        System.out.println(fooList.contains("ID2"));
        System.out.println(fooList.contains("ID5"));
    }   

    public Foo(String id){
        this.id = id;
    }

    @Override
    public boolean equals(Object o){
        if(o instanceof String){
            String toCompare = (String) o;
            return id.equals(toCompare);
        }
        return false;
    }



    @Override
    public int hashCode(){
        return 1;
    }
}

输出: 错误的 错误的 假的

【问题讨论】:

  • 你为什么要检查愚蠢的包含一个字符串?
  • 大约 9 年前我问过这个问题。我现在知道这没有意义,但当时我不明白。感谢这个问题的答案,我现在了解 .equals() 的工作原理。

标签: java equals contains


【解决方案1】:

这是因为您的equals() 不是对称

new Foo("ID1").equals("ID1");

但是

"ID1".equals(new Foo("ID1"));

不正确。这违反了equals() 合约:

equals 方法在非空对象引用上实现等价关系:

  • [...]

  • 它是对称的:对于任何非空引用值xy,当且仅当y.equals(x) 返回truex.equals(y) 应该返回true。

它也不是反身

  • 它是自反的:对于任何非空引用值xx.equals(x) 应该返回 true。
Foo foo = new Foo("ID1");
foo.equals(foo)  //false!

@mbockus 提供了equals() 的正确实现:

public boolean equals(Object o){
  if(o instanceof Foo){
    Foo toCompare = (Foo) o;
    return this.id.equals(toCompare.id);
  }
  return false;
}

但现在你必须将Foo 的实例传递给contains()

System.out.println(fooList.contains(new Foo("ID1")));
System.out.println(fooList.contains(new Foo("ID2")));
System.out.println(fooList.contains(new Foo("ID5")));

最后你应该实现hashCode() 以提供一致的结果(如果两个对象相等,它们必须有相等的hashCode()):

@Override
public int hashCode() {
    return id.hashCode();
}

【讨论】:

  • @ReidMac:我错了,这是关于 equals() 不是对称的,看看我的编辑。 hashCode()在这种情况下没有任何关系,但你还是要遵循这个原则
  • 奇怪的是我们需要使用这个new Foo("ID1"); 约定来使用自定义的equals方法。这背后有什么原因吗?
【解决方案2】:

您的 equals 方法需要随着覆盖 hashCode() 函数而改变。目前,当您需要检查 Foo 对象时,您正在检查您要比较的对象是否为 instanceof String。

public boolean equals(Object o){
    if(o instanceof Foo){
        Foo toCompare = (Foo) o;
        return this.id.equals(toCompare.id);
    }
    return false;
}

如果您使用的是 Eclipse,我建议让 Eclipse 为您生成 hashCode 和 equals,方法是转到 Source -> Generate hashcode() and equals()...

【讨论】:

  • +1,我将您的代码 sn-p 复制到我的答案中,希望您不要介意。
【解决方案3】:

你应该实现 hashCode

@Override
public int hashCode() {
    return id.hashCode();
}

即使 contains 在没有它的情况下也适用于 ArrayList。您的大问题是您的 equals 需要字符串,而不是 Foo 对象,并且您要求包含字符串。如果实现询问列表中的每个弹出是否等于您发送的字符串,那么您的代码可以工作,但实现询问字符串是否等于您的 Foo 对象,当然不是。

使用等于

@Override
public boolean equals(Object o){
    if(o instanceof Foo){
        String toCompare = ((Foo) o).id;
        return id.equals(toCompare);
    }
    return false;
}

然后检查包含

System.out.println(fooList.contains(new Foo("ID1")));

【讨论】:

  • 使用 HashSets,我的 equals() 方法根本不会在 contains() 检查中被调用,直到我也添加了 hashCode() 方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
  • 2017-02-08
  • 1970-01-01
  • 2015-10-06
  • 2020-01-30
相关资源
最近更新 更多