集合框架中的HashSet如何识别重复对象?自定义对象如何使用Hashcode和equals方法?

4

我看了一些代码,发现有一个程序扩展了集合的HashSet类。他们创建了自定义的员工对象,并将其插入Set中,而不实现Comparable或Comparator接口。他们实现了逻辑来识别重复的员工对象,所以如果对象已经存在,他们就可以执行更新操作;如果对象不存在,他们就可以执行插入操作。我试图理解集合如何在没有实现Comparable或Comparator接口的情况下识别重复对象。

我尝试了相同的操作,并尝试重写Hashcode和equals方法以理解Set如何比较对象。我发现当我将相同的对象添加到Set时,它产生相同的Hashcode。但它没有调用我重写的equals方法。

class Employee {
    int id;
    String name;

    Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public int hashCode() {
        System.out.println("HashCode : " + super.hashCode());
    return super.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        System.out.println("Equals : " + super.equals(o));
    return super.equals(o);
    }
}

class LimitedHashSet extends HashSet<Employee> {
    public boolean add(Employee e) {
        if(!super.add(e)){
            return false;
        }
    return true;
    }
}
public class ExtendingHashSet {

    public static void main (String[] args) {
    Employee e0 = new Employee(1,"A");
    Employee e1 = new Employee(2,"B");

    LimitedHashSet obj = new LimitedHashSet();

    System.out.println("Element added ? " + obj.add(e0) + "\n");
    System.out.println("Element added ? " + obj.add(e1) + "\n");
    System.out.println("Element added ? " + obj.add(e0) + "\n");
}
}

Hashcode被用于计算桶(bucket),而equals方法则用于识别是否相等。那么它是如何在这里工作的呢?请查看下面的输出。

输出:

HashCode: 914424520 元素已添加?true

HashCode: 110718392 元素已添加?true

HashCode: 914424520 元素已添加?false


1
@AndyTurner:代码在最后一行调用的是obj.add(e0),而不是obj.add(e3) - Jon Skeet
1
@Aks1316,第二次操作是不是应该添加e3而不是e0 - Andy Turner
另外,你认为在 LimitedHashSet 中重写 add 方法有什么作用?看起来这等同于直接调用 return super.add(e); - Andy Turner
@AndyTurner 不要再添加 e0 了。如果我添加 e3,它将返回 true 并将 e3 添加到集合中,没有任何问题。但是,如果我尝试再次添加 e0,则不会将其添加到集合中,并向我提供 false 的结果,如输出所示。 - Aks 1316
1
@AndrewS,这在多个层面上都是误导性的。关键是Employee,而不是它的hashCode。目前具有相同属性值的Employee对象具有不同的哈希码,根据上面的实现。 - Louis Wasserman
显示剩余5条评论
1个回答

4
基本上,没有什么魔法可言。

HashSet和所有JDK中基于哈希的集合都盲目地使用您对象的hashCodeequals定义的实现来确定对象是否相同。它们只是调用hashCodeequals方法,无论这些方法如何工作,哈希集合都会使用它们。

hashCode方法的使用的一部分-基于哈希数据结构本质功能的一部分-是使哈希集合甚至不必打扰调用equals方法来检查两个对象是否相同。
如果哈希值不同,则对象绝对不相等。所以如果需要,它将调用您重写的equals方法,但在您的代码中,它不需要打扰调用equals方法。此外,在调用equals方法之前,还会检查引用相等性,因为如果两个对象==,则它们肯定.equals于彼此。

当前,您正在添加两次e0。按引用相等,e0 == e0,因此它不需要打扰调用.equals-它只找到匹配的条目,检查它们是否引用相等,并保留现有条目而不添加新元素。顺便说一下,这是正确的行为,因为e0是它自己的副本。


@AndyTurner:不确定你的意思。无论文档是否如此,JDK实现都是这样做的,因为它假定您的实现符合契约,并且这就是为什么OP的程序不会打印“Equals: ...”。 - Louis Wasserman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接