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 可以正确地存储和查找它们。

相关推荐
来自星星的坤2 分钟前
Python 爬虫基础入门教程(超详细)
开发语言·爬虫·python
YKPG5 分钟前
C++学习-入门到精通-【6】指针
开发语言·c++·学习
Timmer丿7 分钟前
kafka学习笔记(四、生产者、消费者(客户端)深入研究(三)——事务详解及代码实例)
java·笔记·学习·kafka
safety_140414 分钟前
c++类【高潮】
开发语言·c++
ghie909021 分钟前
Kotlin中Lambda表达式和匿名函数的区别
java·算法·kotlin
Dxy123931021641 分钟前
Python+OpenCV实现手势识别与动作捕捉:技术解析与应用探索
开发语言·python·opencv
Codingwiz_Joy1 小时前
Day28 -js开发01 -JS三个实例:文件上传 & 登录验证 & 购物商城 & ---逻辑漏洞复现 及 判断js的payload思路
开发语言·javascript·安全·安全性测试
宁酱醇1 小时前
递归函数(斐波那契数列0,1,1,2,3,5,8,13,21,34,55...)
开发语言·python
帮帮志1 小时前
【2025年】基于电脑的jdk1.8通过idea创建springboot2.x版本(非常简洁快速)
java·ide·intellij-idea
寒士obj1 小时前
HashMap中put()方法的执行流程
java·哈希算法·散列表