讲一下equals()与hashcode(),什么时候重写,为什么重写,怎么重写?

参考回答

equals()hashCode() 是 Java 中 Object 类定义的两个重要方法,用于对象比较和哈希操作。

  • equals():用于判断两个对象是否“内容相等”。
  • hashCode():返回对象的哈希值,用于哈希表等数据结构中快速查找。

当需要自定义对象的比较逻辑(例如,两个对象的内容相等但引用不同),就需要同时重写 equals()hashCode(),并确保它们的逻辑保持一致性。


详细讲解与拓展

1. equals() 的默认实现

Object 类中 equals() 的默认实现是使用引用比较,即判断两个对象是否是同一个内存地址。例如:

public class Test {
    public static void main(String[] args) {
        String s1 = new String("Hello");
        String s2 = new String("Hello");
        System.out.println(s1.equals(s2)); // true,因为 String 重写了 equals()
        System.out.println(s1 == s2);      // false,因为引用不同
    }
}

如果你的类不重写 equals(),调用时只会比较引用,而不是对象内容。


2. hashCode() 的默认实现

Object 类中 hashCode() 的默认实现是根据对象的内存地址计算的一个整数值。这意味着两个“内容相等”的对象可能有不同的哈希值,除非重写了 hashCode()


3. equals()hashCode() 的关系

Java 规定了 equals()hashCode() 的关系:

  • 如果两个对象通过 equals() 判断相等,则它们的 hashCode() 值必须相等。
  • 如果两个对象 hashCode() 值相等,它们通过 equals() 不一定相等。

这个关系的重要性体现在哈希表类(如 HashMapHashSet)的工作机制中。


4. 什么时候需要重写 equals()hashCode()

  1. 自定义对象用于集合中(HashMapHashSet 等)
    • 如果没有重写 equals()hashCode(),集合会将“内容相等”的对象当作不同对象处理。
  2. 需要基于对象的内容进行比较
    • 例如,在比较两个人的身份证号是否相等时,需要通过 equals() 判断内容,而不是引用。

5. 如何重写 equals()hashCode()

假设你有一个类 Person,需要根据 idname 判断对象是否相等:

public class Person {
    private int id;
    private String name;

    // 构造方法、Getter 和 Setter
    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true; // 引用相等
        if (obj == null || getClass() != obj.getClass()) return false; // 类型检查

        Person person = (Person) obj; // 类型转换
        return id == person.id && name.equals(person.name); // 比较内容
    }

    @Override
    public int hashCode() {
        int result = Integer.hashCode(id); // 基于 id 的哈希值
        result = 31 * result + name.hashCode(); // 组合 name 的哈希值
        return result;
    }
}

关键点:

  • equals():
    
    • 检查引用相等。
    • 检查类型是否一致。
    • 比较对象的内容。
  • hashCode():
    
    • 使用对象的关键字段生成哈希值。
    • 避免硬编码(31 是常用的乘数,因为它是一个奇质数)。

6. 重写的原则

  1. 一致性
    • 如果 equals() 判断两个对象相等,它们的 hashCode() 必须相等。
    • 如果对象在应用程序生命周期中没有被修改,equals()hashCode() 的结果也必须一致。
  2. 性能
    • hashCode() 的实现应尽量均匀分布,避免产生过多冲突。

7. 举例:重写的重要性

如果不重写 hashCode()equals(),以下代码会有问题:

import java.util.HashSet;

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person(1, "Alice");
        Person p2 = new Person(1, "Alice");

        HashSet<Person> set = new HashSet<>();
        set.add(p1);
        set.add(p2);

        System.out.println(set.size()); // 如果不重写 equals 和 hashCode,输出 2;否则输出 1
    }
}

8. 拓展知识

  • Objects 工具类
    • Java 提供了 Objects.equals() 和 Objects.hash(),可以简化 equals() 和 hashCode()的实现。例如:
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
    
        Person person = (Person) obj;
        return id == person.id && Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
    
  • Lombok 的帮助
    • 使用 Lombok,可以通过注解 @EqualsAndHashCode 自动生成这两个方法,避免手写代码。
  • hashCode() 的优化
    • 对于大型集合中的频繁查询,设计一个高效的 hashCode() 函数可以显著提升性能。

9. 总结

  • 重写 equals()hashCode() 是为了让对象的比较逻辑更贴近业务需求,尤其是在集合类中使用时。
  • 两者必须保持一致性,即两个对象相等时它们的哈希值也必须相等。
  • 重写时,应遵守规范并尽量使用工具类来简化实现。

发表评论

后才能评论