equals() 和 hashCode()

作为 Java 开发者,我们经常会用到 equals()hashCode() 这两个方法。 它们是 Object 类中定义的基础方法,看似简单,但如果理解不透彻,很容易在实际开发中踩坑。 本文将深入探讨这两个方法的作用、区别、以及如何正确地重写它们。

1. equals() 方法:判断对象是否相等

equals() 方法用于比较两个对象是否"相等"。 默认情况下,Object 类的 equals() 方法比较的是两个对象的 引用 是否相等,也就是说,只有当两个对象指向内存中的同一个地址时,equals() 方法才会返回 true

复制代码
Object obj1 = new Object();
Object obj2 = obj1; // obj2 指向与 obj1 相同的对象

System.out.println(obj1.equals(obj2)); // 输出 true (引用相等)

Object obj3 = new Object(); // obj3 指向一个新的对象

System.out.println(obj1.equals(obj3)); // 输出 false (引用不相等)

然而,在很多情况下,我们希望根据对象的 内容 来判断是否相等,而不是根据引用。 例如,对于 String 类,我们希望只要两个字符串的内容相同,就认为它们相等。 这时,我们就需要 重写 equals() 方法。

复制代码
String str1 = new String("hello");
String str2 = new String("hello");

System.out.println(str1.equals(str2)); // 输出 true (String 类重写了 equals() 方法)

重写 equals() 方法的原则:

  • 自反性: 对于任何非空对象 xx.equals(x) 必须返回 true
  • 对称性: 对于任何非空对象 xy,如果 x.equals(y) 返回 true,那么 y.equals(x) 必须返回 true
  • 传递性: 对于任何非空对象 xyz,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 必须返回 true
  • 一致性: 对于任何非空对象 xy,如果 xy 的比较操作所用到的信息没有被修改,那么多次调用 x.equals(y) 要么始终返回 true,要么始终返回 false
  • 非空性: 对于任何非空对象 xx.equals(null) 必须返回 false

一个重写 equals() 方法的例子:

复制代码
import java.util.Objects;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }
}

2. hashCode() 方法:生成对象的哈希码

hashCode() 方法用于返回对象的哈希码值。 哈希码是一个整数,主要用于在哈希表(例如 HashMapHashSet)中快速查找对象。

默认情况下,Object 类的 hashCode() 方法通常(但不保证)返回基于对象 内存地址 的一个整数值。

hashCode() 方法的通用约定:

  • 一致性: 在应用程序执行期间,只要对象的 equals 方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode 方法都必须始终如一地返回同一个整数。
  • 相等性: 如果两个对象根据 equals(Object) 方法是相等的,那么调用这两个对象中任意一个对象的 hashCode 方法,都必须产生同一个整数结果。
  • 可选性: 如果两个对象根据 equals(Object) 方法是不相等的,那么调用这两个对象中任意一个对象的 hashCode 方法,不 要求 产生不同的整数结果。 但是,程序员应该意识到,给不相等的对象产生截然不同的整数结果,有可能提高散列表(hash table)的性能。

重点:如果两个对象 equals() 相等,那么它们的 hashCode() 必须相等!

3. 为什么需要同时重写 equals()hashCode()

这是本文最重要的部分。 当你重写 equals() 方法时,必须同时重写 hashCode() 方法,以保证 hashCode() 方法的通用约定得到满足。

不重写 hashCode() 会导致什么问题?

如果你只重写了 equals() 方法,而没有重写 hashCode() 方法,那么相等的对象(根据 equals() 方法判断)会因为 Object 类的默认 hashCode() 实现而产生不同的哈希码。 这会导致在使用哈希表(例如 HashMapHashSet)时出现问题,例如查找失败或存储重复的对象。

举例说明:

复制代码
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }

    // 缺少 hashCode() 方法!
}

public class Main {
    public static void main(String[] args) {
        Map<Person, String> map = new HashMap<>();

        Person person1 = new Person("Alice", 30);
        Person person2 = new Person("Alice", 30);  // 与 person1 相等

        map.put(person1, "Alice's information");

        // 尝试获取与 person1 相等的 person2 的信息
        String information = map.get(person2);

        System.out.println(information);  // 输出 null!
    }
}

展开

在这个例子中,person1person2 根据 equals() 方法是相等的。 但是,由于没有重写 hashCode() 方法,它们具有不同的哈希码,因此被 HashMap 存储在不同的哈希桶中。 当你尝试使用 person2HashMap 中获取信息时,HashMap 会根据 person2 的哈希码找到一个错误的哈希桶,导致查找失败,返回 null

正确的做法:

复制代码
import java.util.Objects;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

现在,person1person2 具有相同的哈希码,HashMap 可以正确地存储和查找它们。

相关推荐
风中飘爻1 小时前
JavaScript:BOM编程
开发语言·javascript·ecmascript
kyle~1 小时前
ROS2---std_msgs基础消息包
开发语言·python·机器人·ros·机器人操作系统
满怀10151 小时前
【NumPy科学计算引擎:从基础操作到高性能实践】
开发语言·python·numpy
我命由我123452 小时前
35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)
java·服务器·开发语言·jvm·后端·架构·java-ee
&zzz2 小时前
Python生成exe
开发语言·python
Chandler242 小时前
Go:方法
开发语言·c++·golang
CopyLower3 小时前
分布式ID生成方案的深度解析与Java实现
java·开发语言·分布式
随便@_@4 小时前
基于MATLAB/simulink的信号调制仿真--AM调制
开发语言·matlab·simulink·移动仿真
爱代码的小黄人4 小时前
深入解析系统频率响应:通过MATLAB模拟积分器对信号的稳态响应
开发语言·算法·matlab
vsropy4 小时前
matlab安装python API 出现Invalid version: ‘R2022a‘,
开发语言·python