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

时间:2010-11-07 00:49:48

标签: java equals hashcode contains hashset

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

在我写的一个小程序中,我生成了非常相似的对象,这些对象在创建时会有一个非常具体的id(string或者在我的上一次迭代中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());
    }
}

产生以下输出:

true
false
true
true    

显然,contains不使用equals提供的L函数,或者我对此概念有一些重大误解......

我用openjdk(ubuntu中包含的当前版本)和Win7上Oracle的官方当前java测试了它

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

  

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)

有任何想法或建议吗?

3 个答案:

答案 0 :(得分:28)

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

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

答案 1 :(得分:3)

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

此外,这就是为什么你的JRE支持时你真的应该使用@Override注释。这样,如果您打算覆盖现有方法时意外实施新方法,编译器会抱怨。

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

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

答案 2 :(得分:3)

当您向对象添加对象时,它会在内部调用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。