【问题标题】:How HashSet of collection framework identifies duplicate objects? How Hashcode and equals work with custom objects?集合框架的HashSet如何识别重复对象? Hashcode 和 equals 如何与自定义对象一起使用?
【发布时间】:2019-09-24 08:06:59
【问题描述】:

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

我已经尝试过同样的方法,也尝试过重写 Hashcode 和 equals 方法来了解 Set 如何比较对象。我发现的一件事是,当我添加相同的对象来设置它生成相同的哈希码时。但它没有调用我已经覆盖的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方法识别其是否相等。那么它是如何在这里工作的。请检查下面的输出。

输出:

哈希码:914424520 添加元素 ?真的

哈希码:110718392 添加元素 ?真的

哈希码:914424520 添加元素 ?假的

【问题讨论】:

  • @AndyTurner:代码在最后一行调用obj.add(e0),而不是obj.add(e3)
  • @Aks1316 你的意思是第二次添加e3而不是e0
  • 另外:您认为在LimitedHashSet 中覆盖add 有什么作用?看起来就相当于只调用return super.add(e);
  • @AndyTurner 不再添加 e0。如果我添加 e3 它将返回 true 并将 e3 添加到 set 没有任何问题。但是,如果我尝试再次添加 e0,则它不会将其添加到集合中并为我提供结果 false,如输出所示。
  • @AndrewS 这在几个层面上具有误导性。关键是Employee,而不是hashCode。给定上面的实现,具有相同属性值的 Employee 对象当前具有不同的哈希码。

标签: java arraylist collections


【解决方案1】:

从根本上说,没有魔法。

HashSet 和所有基于 JDK 哈希的集合盲目地使用对象的 hashCodeequals 定义的实现来确定对象是否相同。他们只是调用 hashCodeequals 方法,不管这些方法如何工作,这就是哈希集合使用的方法。

使用hashCode 方法的部分意义——首先是基于散列的数据结构的部分意义——是使散列不太可能基于集合甚至不得不费心调用equals 方法来检查两个对象是否相同。如果哈希值不同,那么对象肯定不相等。因此,如果需要,它调用您已覆盖的equals 方法,但在您的代码中,它不需要调用equals。此外,它还在调用equals 方法之前检查引用相等性,因为如果两个对象是==,那么它们肯定是.equals

目前,您添加了两次e0。通过引用相等,e0 == e0,所以它不会打扰调用.equals——它只是找到匹配的条目,检查它们是否引用相等,并保留现有条目而不添加新元素。顺便说一句,这是正确的行为,因为 e0 是其自身的副本。

【讨论】:

  • @AndyTurner:不确定你的意思。不管它是不是文档所说的,它都是 JDK 实现所做的,因为它假设您的实现是符合合同的,这就是为什么 OP 的程序不打印“Equals: ...”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
相关资源
最近更新 更多