【问题标题】:Weird Set.contains() behavior奇怪的 Set.contains() 行为
【发布时间】:2013-05-29 03:11:03
【问题描述】:

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

我有一些对象是我的应用程序的数据库对象的容器。这些对象都有唯一的integer idhashCode()equals() 由整数 id 定义(用于存储在哈希集中)。

嗯,我希望能够检查一个哈希集是否包含只给定 id 的对象。 当然,我可以创建一个对象的新实例并以这种方式进行检查。但是,只是为了好玩,我想看看我是否可以完成它。当然,这对于 hashmap 来说也是微不足道的,所以这真的不是一个重要的问题,只是为了好玩和知识。

所以,我创建了一个类,并尝试在 integer 上调用 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方法中进行验证,是的,它甚至没有被调用。

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

Here
Yes
Here
Yes

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

Here
Yes
Here
No

但是,输出是:

Here
Yes
No

注意,“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");
        }
    }

【问题讨论】:

  • 它跳过了equals,因为它调用了integer.equals(e)。来自 javadoc: (o==null ? e==null : o.equals(e))".

标签: java contains hashset


【解决方案1】:

这是因为 比较是在提供的对象上进行的 而不是集合中的元素。来自HashSet#contains(Object)

如果此集合包含指定元素,则返回 true。更正式地说,当且仅当此集合包含元素 e 满足 (o==null ? e==null : o.equals(e)).

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

【讨论】:

  • 太棒了!是的,这完美地解释了它。如果contains() 基于e.equals(o),那么我的可能会工作。但是因为它基于给定对象(o)的相等性,所以我不走运。伟大的观察!这个真的让我摸不着头脑。
  • 这也帮助我意识到我的equals() 方法不是对称的。也就是说,e.equals(o) 将返回 true,但 o.equals(e) 将返回 false。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-08
  • 2015-07-20
  • 2010-10-03
  • 2021-07-12
  • 2013-10-04
  • 2019-01-23
相关资源
最近更新 更多