2

嗨,我是 java 新手,因为我知道 set 集合不会重复,并且当集合中已经存在元素时,它的 contains 方法应该返回 true。我正在尝试在程序下运行,但我得到了意想不到的结果。

public class UserDefinedName {
    private final String first, last;

    public UserDefinedName(String first, String last) {
        this.first = first;
        this.last = last;
    }

    public boolean equals(Object o) {
        if (!(o instanceof UserDefinedName))
            return false;
        UserDefinedName n = (UserDefinedName) o;
        return n.first.equals(first) && n.last.equals(last);
    }

    public static void main(String[] args) {
        Set<UserDefinedName> s = new HashSet<UserDefinedName>();
        s.add(new UserDefinedName("Carballo", "Videl"));
        System.out.println(s.contains(new UserDefinedName("Carballo", "Videl")));
    }
}

我期待输出true但程序打印false。我做错了什么?

4

5 回答 5

8

形成java文档

如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。

如果 hashCode() 方法未被覆盖,则使用 Object 的 hashCode() 方法,这是默认实现。

在您的情况下,您有覆盖 equals 方法,但您使用 hashCode() 的默认实现,因为您没有在 UserDefinedName 类中覆盖 hashCode() 方法。

UserDefinedName 类重写了 equals 方法,hashCode 契约要求相等的对象具有相等的哈希码。要履行此合同,您必须在覆盖 equals 时覆盖 hashCode

添加以下代码,它将起作用。

public int hashCode() {
return 37 * first.hashCode() + last.hashCode(); 
}
于 2013-06-14T08:49:53.767 回答
1

当你覆盖 equals 方法时,也总是覆盖 hashcode 方法。一个简单的规则是,如果两个对象被认为是相等的,那么它们应该返回相同的哈希码。

我在 eclipse 的帮助下获取了你的代码并生成了 equals 和 hashcode

import java.util.HashSet;
import java.util.Set;

public class UserDefinedName {
private final String first, last;

public UserDefinedName(String first, String last) {
    this.first = first;
    this.last = last;
}

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((first == null) ? 0 : first.hashCode());
    result = prime * result + ((last == null) ? 0 : last.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    UserDefinedName other = (UserDefinedName) obj;
    if (first == null) {
        if (other.first != null)
            return false;
    } else if (!first.equals(other.first))
        return false;
    if (last == null) {
        if (other.last != null)
            return false;
    } else if (!last.equals(other.last))
        return false;
    return true;
}

public static void main(String[] args) {
    Set<UserDefinedName> s = new HashSet<UserDefinedName>();
    s.add(new UserDefinedName("Carballo", "Videl"));
    System.out.println(s.contains(new UserDefinedName("Carballo", "Videl")));
    UserDefinedName obj1 = new UserDefinedName("prasad", "kharkar");
    UserDefinedName obj2 = new UserDefinedName("prasad", "kharkar");

    System.out.println(obj1.equals(obj2));
    System.out.println(obj1.hashCode());
    System.out.println(obj2.hashCode());

    }
}

输出是

true
true
-1072813416
-1072813416
于 2013-06-14T08:48:46.300 回答
1

因为 anhashSet使用对象的 hashcode 值来存储它。所以它要求你必须在你自己的类中重写方法 hashCode 。由于您没有覆盖它,它使用从对象类继承的 hashCode 方法。

对象类的哈希码是使用对象的内存地址计算的,即:

UserDefinedName p = new UserDefinedName("Carballo", "Videl");
System.out.println(p);
System.out.println(0x1e5e2c3);
System.out.println(p.hashCode());

输出 :

UserDefinedName@1e5e2c3
31843011
31843011

因此,如果您尝试这样做,您将看到它输出 true :

Set<UserDefinedName> s = new HashSet<UserDefinedName>();
UserDefinedName p = new UserDefinedName("Carballo", "Videl");
s.add(p);
System.out.println(s.contains(p));

现在,如果您想比较您的 User 类的正确性,您将不得不覆盖您的hashCode方法(您可以使用 eclipse 生成它)以hashCode逐个字段生成您的对象。

如果你的类有这个方法,这将为你提供的代码打印 true。

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((first == null) ? 0 : first.hashCode());
        result = prime * result + ((last == null) ? 0 : last.hashCode());
        return result;
    }
于 2013-06-14T08:37:11.980 回答
0

除了equals,您还需要实现hashcode() 方法。

HashSet 是一种内部使用 HashMap 的数据结构。在 HashMap 中,对象存储在键值对中。但是,HashSet 只接受键,对应的值是一个空的虚拟对象(这当然由 JVM 负责)。现在,要使这种散列机制正常工作,您需要实现 equals() 和 hashCode() 方法。根据JDK文档

该类实现了由哈希表(实际上是 HashMap 实例)支持的 Set 接口。它不保证集合的迭代顺序;特别是,它不保证订单会随着时间的推移保持不变。此类允许空元素。

此类为基本操作(添加、删除、包含和大小)提供恒定的时间性能,假设哈希函数将元素正确地分散在桶中。迭代这个集合需要的时间与 HashSet 实例的大小(元素的数量)加上支持 HashMap 实例的“容量”(桶的数量)的总和成正比。

您可能想查看“Effective Java - Second Edition - Chapter 3 - Methods common to all objects”。这将使您清楚地了解实现 equals() 和 hashCode() 方法的最佳实践。

于 2013-06-14T08:39:14.970 回答
0

HashSet需要同时覆盖hashCodeequals方法。

于 2013-06-14T08:40:12.150 回答