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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4115978/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:47:10  来源:igfitidea点击:

Java Hashset.contains() produces mysterious result

javaequalshashcodecontainshashset

提问by Jochen

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:

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

In a small program I was writing, I was generating very similar objects, which, when created, would have a very specific id (a stringor 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:

在我编写的一个小程序中,我生成了非常相似的对象,这些对象在创建时将具有非常具体的 id(astring或在我的上一次迭代中 a long)。因为每个对象都会产生新的对象,所以我想过滤掉我已经创建的所有对象。因此,我开始将每个新对象的 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());
    }
}

produces following output:

产生以下输出:

true
false
true
true    

so apparently, containsdoes not use the equalsfunction provided by L, or I have some major misunderstanding of the concept ...

很明显,contains没有使用equals提供的功能L,或者我对这个概念有一些重大误解......

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

我用 openjdk(包含在 ubuntu 中的当前版本)和来自 Oracle 在 Win7 上的官方当前 Java 对其进行了测试

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

完整的官方 java-api 文档HashSet.contains()

public boolean contains(Object o)

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

public boolean contains(Object 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)

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

Any ideas or suggestions?

有什么想法或建议吗?

回答by SLaks

Your equalsmethod 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 hashSetclass calls equals, it resolves to the base Object.equalsmethod. When you call equals, you call your overload because aand bare both declaredas Linstead of Object.

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

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

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

回答by bcat

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

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

Also, this is why you really should use @Overrideannotations 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.

此外,这就是为什么@Override当您的 JRE 支持注释时,您确实应该使用注释。这样,如果您在打算覆盖现有方法时不小心实现了一个新方法,编译器就会抱怨。

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.)

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

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

回答by Jagadeesh

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

当您将对象添加到集合时,它会在内部调用equalshashCode方法。您必须覆盖这两个方法。例如,我采用了一个带有name, id, 的bean 类designation,然后创建并添加了一个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()calls internally the equalsand hashCodemethods. So you have to override these two methods in your bean class.

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;
}   

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

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