Java Hashset.contains() 产生神秘的结果

发布于 2024-09-30 23:36:35 字数 1800 浏览 4 评论 0原文

我通常不使用 Java 编写代码,但最近我开始别无选择。我可能对如何正确使用 HashSet 有一些重大误解。所以我所做的事情可能完全是错误的。不过,我很感激您可能提供的任何帮助。所以实际的问题是:

在我编写的一个小程序中,我生成了非常相似的对象,这些对象在创建时将有一个非常特定的 id(一个 string 或在我的最后一次迭代中一个 )。因为每个对象都会产生新的对象,所以我想过滤掉所有已经创建的对象。因此,我开始将每个新对象的 id 放入我的 Hash(Set) 中,并使用 HashSet.contains() 测试是否之前创建了对象。这是完整的代码:

// hashtest.java
import java.util.HashSet;

class L {
    public long l;
    public L(long l) {
        this.l = l;
    }
    public int hashCode() {
        return (int)this.l;
    }
    public boolean equals(L other) {
        return (int)this.l == (int)other.l;
    }
}

class hashtest {
    public static void main(String args[]) {
        HashSet<L> hash = new HashSet<L>();
        L a = new L(2);
        L b = new L(2);
        hash.add(a);
        System.out.println(hash.contains(a));
        System.out.println(hash.contains(b));
        System.out.println(a.equals(b));
        System.out.println(a.hashCode() == b.hashCode());
    }
}

产生以下输出:

true
false
true
true    

所以显然,contains没有使用L提供的equals函数,或者我有一些主要的对概念的误解...

我使用 openjdk(ubuntu 中包含的当前版本)和 Win7 上 Oracle 的官方当前 java 对其进行了测试,

以确保 HashSet.contains() 的官方 java-api 文档的完整性:

公共布尔包含(对象o)

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

http:// download.oracle.com/javase/6/docs/api/java/util/HashSet.html#contains(java.lang.Object)

有什么想法或建议吗?

I don't usually code in Java, but recently I started not having a choice. I might have some major misunderstanding of how to properly use HashSet. So it might be possible something I did is just plain wrong. However I'm grateful for any help, you might offer. So the actual problem:

In a small program I was writing, I was generating very similar objects, which, when created, would have a very specific id (a string or in my last iteration a long). Because each object would spawn new objects, I wanted to filter out all those I already created. So I started throwing the id of every new object into my Hash(Set) and testing with HashSet.contains(), if an object was created before. Here is the complete code:

// hashtest.java
import java.util.HashSet;

class L {
    public long l;
    public L(long l) {
        this.l = l;
    }
    public int hashCode() {
        return (int)this.l;
    }
    public boolean equals(L other) {
        return (int)this.l == (int)other.l;
    }
}

class hashtest {
    public static void main(String args[]) {
        HashSet<L> hash = new HashSet<L>();
        L a = new L(2);
        L b = new L(2);
        hash.add(a);
        System.out.println(hash.contains(a));
        System.out.println(hash.contains(b));
        System.out.println(a.equals(b));
        System.out.println(a.hashCode() == b.hashCode());
    }
}

produces following output:

true
false
true
true    

so apparently, contains does not use the equals function provided by L, or I have some major misunderstanding of the concept ...

I tested it with openjdk (current version included in ubuntu) and the official current java from Oracle on Win7

for completeness official java-api documentation for HashSet.contains():

public boolean contains(Object o)

Returns true if this set contains the
specified element. More formally,
returns true if and only if this set
contains an element e such that
(o==null ? e==null : o.equals(e)).

http://download.oracle.com/javase/6/docs/api/java/util/HashSet.html#contains(java.lang.Object)

Any ideas or suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风吹短裙飘 2024-10-07 23:36:35

您的 equals 方法需要采用 Object
因为您将其声明为采用 L,所以它变成了额外的重载,而不是覆盖该方法。
因此,当 hashSet 类调用 equals 时,它会解析为基类 Object.equals 方法。当您调用 equals 时,您会调用重载,因为 ab声明L< /code> 而不是 Object

为了防止将来出现此问题,您应该在重写方法时添加 @Override
这样,如果它实际上不是覆盖,编译器会警告您。

Your equals method needs to take an Object.
Because you declared it as taking an L, it becomes an additional overload instead of overriding the method.
Therefore, when the hashSet class calls equals, it resolves to the base Object.equals method. When you call equals, you call your overload because a and b are both declared as L instead of Object.

To prevent this issue in the future, you should add @Override whenever you override a method.
This way, the compiler will warn you if it isn't actually an override.

衣神在巴黎 2024-10-07 23:36:35

您实际上并没有重写Object.equals;相反,您正在定义一个具有相同名称但不同参数的新方法。请注意,Object.equals 采用 Object 参数,而 equals 方法采用 L 参数。如果您重写 equals 方法以获取 Object 并在运行时对 L 执行必要的类型检查/转换,那么您的代码将按您的预期工作。

另外,这就是为什么你真的应该使用 @Override只要您的 JRE 支持,就可以使用 注释。这样,如果您在打算覆盖现有方法时不小心实现了新方法,编译器就会抱怨。

举例来说,这个 equals 方法应该可以正常工作。 (并且,在一个不相关的注释中,如果要比较的对象为空,则它不会失败。)

@Override
public boolean equals(Object other) {
    return other != null && other instanceof L && this.l == ((L)other).l;
}

You're not actually overriding Object.equals; instead, you're defining a new method with the same name but different parameters. Notice that Object.equals takes an Object argument, while your equals method takes an L argument. If you rewrite your equals method to take an Object and perform the necessary type-checking/casting to L at runtime, then your code is work as you expect.

Also, this is why you really should use @Override annotations whenever your JRE supports them. That way, the compiler will complain if you accidentally implement a new method when you intend to override an existing one.

By way of an example, this equals method should work correctly. (And, on an unrelated note, it won't fail if the object being compared to is null.)

@Override
public boolean equals(Object other) {
    return other != null && other instanceof L && this.l == ((L)other).l;
}
小ぇ时光︴ 2024-10-07 23:36:35

当您将对象添加到集合中时,它会在内部调用 equalshashCode 方法。您必须重写这两个方法。例如,我采用了一个带有 nameiddesignation 的 bean 类,然后创建并添加了一个 employee 对象。

HashSet<Employee> set = new HashSet<Employee>();
Employee employee = new Employee();
employee.setId(1);
employee.setName("jagadeesh");
employee.setDesignation("programmer");
set.add(employee);
Employee employee2 = new Employee();
employee2.setId(1);
employee2.setName("jagadeesh");
employee2.setDesignation("programmer");
set.add(employee2);

set.add() 在内部调用 equalshashCode 方法。所以你必须在你的bean类中重写这两个方法。

@Override
public int hashCode(){
    StringBuffer buffer = new StringBuffer();
    buffer.append(this.name);
    buffer.append(this.id);
    buffer.append(this.designation);
    return buffer.toString().hashCode();
}
@Override
public boolean equals(Object object){
    if (object == null) return false;
    if (object == this) return true;
    if (this.getClass() != object.getClass())return false;
    Employee employee = (Employee)object;
    if(this.hashCode()== employee.hashCode())return true;
   return false;
}   

这里我们重写了equals()hashCode()。当您将对象添加到 HashSet 方法时,它会在内部迭代所有对象并调用 equals 方法。因此,我们重写hashCode,它将每个对象hashCode与其当前hashCode进行比较,如果两者相等则返回true,否则返回false。

When you are adding objects to a set it internally calls equals and hashCode methods. You have to override these two methods. For example I have taken one bean class with name,id,designation, then created and added an employee object.

HashSet<Employee> set = new HashSet<Employee>();
Employee employee = new Employee();
employee.setId(1);
employee.setName("jagadeesh");
employee.setDesignation("programmer");
set.add(employee);
Employee employee2 = new Employee();
employee2.setId(1);
employee2.setName("jagadeesh");
employee2.setDesignation("programmer");
set.add(employee2);

set.add() calls internally the equals and hashCode methods. So you have to override these two methods in your bean class.

@Override
public int hashCode(){
    StringBuffer buffer = new StringBuffer();
    buffer.append(this.name);
    buffer.append(this.id);
    buffer.append(this.designation);
    return buffer.toString().hashCode();
}
@Override
public boolean equals(Object object){
    if (object == null) return false;
    if (object == this) return true;
    if (this.getClass() != object.getClass())return false;
    Employee employee = (Employee)object;
    if(this.hashCode()== employee.hashCode())return true;
   return false;
}   

Here we are overriding equals() and hashCode(). When you add an object to the HashSet method it internally iterates all objects and calls the equals method. Hence we overrid hashCode, it compares every objects hashCode with its current hashCode and returns true if both are equal, else it returns false.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文