.Contains() 方法不调用 Overridden equals 方法

Posted

技术标签:

【中文标题】.Contains() 方法不调用 Overridden equals 方法【英文标题】:.Contains() method not calling Overridden equals method 【发布时间】: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() 的工作原理。 【参考方案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() 方法。

以上是关于.Contains() 方法不调用 Overridden equals 方法的主要内容,如果未能解决你的问题,请参考以下文章

list.contains方法既然是调用equ 方法 还用重写 hashcod吗

Groovy集合遍历 ( 调用集合的 every 方法判定集合中的所有元素是否符合闭包规则 | =~ 运算符等价于 contains 函数 | 代码示例 )

java 类中定义接口的调用方法

list.contains

如何在单元测试期间覆盖 IQueryable 的 Contains 方法?

为啥 set.contains 不使用覆盖的 equals() 方法?