Java 奇怪的Set.contains()行为

Java 奇怪的Set.contains()行为,java,contains,hashset,Java,Contains,Hashset,我最初是为了测试一个基于理论、最佳实践的问题,我想在这里问这个问题,但在这个过程中,我在java.Set类中发现了一些有趣的行为。起初,我想知道这种方法的任何潜在缺陷,但现在我发现它根本不起作用,我想知道原因。 我有一些对象是我的应用程序数据库对象的容器。所有对象都具有唯一的整数id,并且hashCode()和equals()由整数id定义(用于存储在hashset中) 我希望能够检查hashset是否只包含给定id的对象。 当然,我可以创建一个新的对象实例并进行检查。但是,只是为了好玩,我想看

我最初是为了测试一个基于理论、最佳实践的问题,我想在这里问这个问题,但在这个过程中,我在java.Set类中发现了一些有趣的行为。起初,我想知道这种方法的任何潜在缺陷,但现在我发现它根本不起作用,我想知道原因。

我有一些对象是我的应用程序数据库对象的容器。所有对象都具有唯一的
整数id
,并且
hashCode()
equals()
由整数id定义(用于存储在hashset中)

我希望能够检查hashset是否只包含给定id的对象。 当然,我可以创建一个新的对象实例并进行检查。但是,只是为了好玩,我想看看我是否能完成它。当然,这对于hashmap来说也很简单,所以这其实不是一个重要的问题,只是为了好玩和知识。

因此,我创建了一个类,并尝试对
整数
调用
contains()
,而不是对象的实例。当然,Netbeans对此给出了一个有趣的警告

Suspicious call to java.util.Collection.contains:
Given object cannot contain instances of int (expected Person)
忽略错误并运行代码,我震惊地发现Java甚至没有调用equals方法。我将调试
System.out.println()
s放在equals方法中进行验证,是的,它甚至没有被调用

在下面发布的代码中,预期输出应该是(如果我的理论是正确的):

或者(如果我的理论是不正确的):

然而,输出是:

注意,在“no”之前没有“Here”来证明equals方法甚至没有被调用

有人能解释吗?我总是被告知将此添加到
equals()
以提高效率:

if (!(obj instanceof Person))
    return false;
但是,如果在这种情况下甚至没有调用
equals()
,那么这将是毫无意义的

以下是SSCCE:

谢谢你抽出时间

    import java.util.LinkedHashSet;
    import java.util.Set;

    /**
     *
     * @author Ryan
     */
    public class Test7 {
        public static void main(String[] args) {
            class Person {
                public final int id;
                public final String name;

                public Person(int id, String name) {
                    this.id = id;
                    this.name = name;
                }

                @Override
                public boolean equals(Object obj) {
                    System.out.println("Here");
                    if (this == obj)
                        return true;
                    if (obj instanceof Person)
                        return id  == ((Person)obj).id;
                    else if(obj instanceof Integer)
                        return id == (Integer)obj;
                    else {
                        System.out.println("Returning False");
                        return false;
                    }
                }

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

            Set<Person> set = new LinkedHashSet<Person>();
            set.add(new Person(1, "Bob"));
            set.add(new Person(2, "George"));
            set.add(new Person(3, "Sam"));


            if(set.contains(new Person(1, "Bob")))
                System.out.println("Yes");
            else
                System.out.println("No");

            if(set.contains(1))
                System.out.println("Yes");
            else
                System.out.println("No");
        }
    }
import java.util.LinkedHashSet;
导入java.util.Set;
/**
*
*@作者Ryan
*/
公共类Test7{
公共静态void main(字符串[]args){
班主任{
公共最终int id;
公共最终字符串名;
公众人物(整数id,字符串名称){
this.id=id;
this.name=名称;
}
@凌驾
公共布尔等于(对象obj){
System.out.println(“此处”);
if(this==obj)
返回true;
if(人员的obj实例)
返回id==((人)对象)id;
else if(obj instanceof Integer)
返回id==(整数)obj;
否则{
System.out.println(“返回False”);
返回false;
}
}
@凌驾
公共int hashCode(){
返回id;
}
}
Set Set=newlinkedhashset();
集合。添加(新人物(1,“Bob”);
增加(新的人(2,“乔治”);
集合。添加(新人物(3,“Sam”);
if(集合包含(新的人(1,“Bob”))
System.out.println(“是”);
其他的
系统输出打印项次(“否”);
if(集合包含(1))
System.out.println(“是”);
其他的
系统输出打印项次(“否”);
}
}

这是因为比较是在提供的对象上进行的,而不是在集合中的元素上进行的。发件人:

如果此集合包含指定的元素,则返回true。更正式地说,当且仅当该集合包含一个元素e,使得(o==null?e==null:o.equals(e))时,返回true


因此,在您的示例中,您将进行类似于
integer.equals(person)
的比较。因此,如果您的集合包含
Person
对象,则将永远不会检查
if(obj instanceof Integer)
条件,但如果您的集合包含
Integer
对象,则将满足该条件,并将对其进行检查。

它跳过等号,因为它调用Integer.equals(e)。来自javadoc:(o==null?e==null:o.equals(e))。太棒了!是的,这很好地解释了它。如果
contains()
基于
e.equals(o)
,那么我的可能会起作用。但因为它基于给定对象的相等(
o
),我运气不好。观察得很好!这一次让我抓狂。这也帮助我认识到我的
equals()
方法是不对称的。也就是说,
e.equals(o)
将返回true,但
o.equals(e)
将返回false。
Here
Yes
No
if (!(obj instanceof Person))
    return false;
    import java.util.LinkedHashSet;
    import java.util.Set;

    /**
     *
     * @author Ryan
     */
    public class Test7 {
        public static void main(String[] args) {
            class Person {
                public final int id;
                public final String name;

                public Person(int id, String name) {
                    this.id = id;
                    this.name = name;
                }

                @Override
                public boolean equals(Object obj) {
                    System.out.println("Here");
                    if (this == obj)
                        return true;
                    if (obj instanceof Person)
                        return id  == ((Person)obj).id;
                    else if(obj instanceof Integer)
                        return id == (Integer)obj;
                    else {
                        System.out.println("Returning False");
                        return false;
                    }
                }

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

            Set<Person> set = new LinkedHashSet<Person>();
            set.add(new Person(1, "Bob"));
            set.add(new Person(2, "George"));
            set.add(new Person(3, "Sam"));


            if(set.contains(new Person(1, "Bob")))
                System.out.println("Yes");
            else
                System.out.println("No");

            if(set.contains(1))
                System.out.println("Yes");
            else
                System.out.println("No");
        }
    }