相等原则:equals()和hashCode()

在编程语言的宇宙中有一世界叫做 JavaJava 中有一个超级老祖,叫做 Object, 他是所有类的共同的父类,所有对象均隐性继承了此类。 Object 有两个方法,equals()hashCode(); 这两个方法也是隐性的传承给了所有的对象。

一、equals()

1.1 equals()简介

equals()方法是Java中的一个方法,用于比较两个对象是否相等。它是在Object类中定义的,因此所有的Java类都继承了这个方法。

默认情况下,equals()方法比较两个对象的引用是否相等,也就是它们是否指向了内存中的同一个对象。这是因为Object类的equals()方法实现如下:

java 复制代码
public boolean equals(Object obj) {
    return (this == obj);
}

然而,在实际开发中,我们通常需要根据对象的属性来确定它们是否相等,而不仅仅是比较引用。这就需要我们在自定义类中重写equals()方法,以便根据业务逻辑来定义相等性比较。

1.2 equals()重写(自定义类对象内容是否相等)

通过重写equals()方法,我们可以根据自定义的相等性逻辑来比较对象,而不仅仅是比较引用。这样可以提高对象比较的灵活性和准确性。在使用集合类、比较对象等场景时,正确实现equals()方法非常重要。

在重写equals()方法时,我们通常需要考虑以下几个方面:

graph LR A(重写equals方法遵循原则) B(检查传入的参数是否为null 如果是null 则直接返回false) C(检查传入的参数是否为当前类的实例 如果不是 则返回false) D(将传入的参数转换为当前类的类型 并比较对象的属性是否相等) E(对于引用类型的属性 可以使用它们的equals方法进行比较) F(对于基本类型的属性 可以使用 == 运算符进行比较) A ---> B A ---> C A ---> D A ---> E A ---> F style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px style F fill:#B2FFFF,stroke:#B2FFFF,stroke-width:2px

下面是一个示例,演示了如何重写equals()方法:

java 复制代码
public class Person {
    private String name;
    private int age;

    // 构造函数、getter和setter方法等省略

    @Override
    public boolean equals(Object obj) {
        // 检查传入的参数是否为null
        if (obj == null) {
            return false;
        }

        // 检查传入的参数是否为当前类的实例
        if (getClass() != obj.getClass()) {
            return false;
        }

        // 将传入的参数转换为当前类的类型
        Person other = (Person) obj;

        // 比较对象的属性是否相等
        return name.equals(other.name) && age == other.age;
    }
}

二、hashCode()

2.1 hashCode()简介

hashCode()方法是Java中的一个方法,用于返回对象的哈希码(整数值)。它是在 Object 类中定义的,因此所有的 Java 类都继承了这个方法。

哈希码是根据对象的属性计算得出的一个整数值。它通常用于在哈希表等数据结构中进行对象的存储和查找。哈希表是一种根据键值对存储和检索数据的数据结构,它通过将键转换为哈希码来确定存储位置,从而实现快速的数据访问。

Java 中,哈希表的实现类(如 HashMapHashSet 等)使用了对象的哈希码来决定对象在表中的存储位置。因此,重写equals()方法时通常也需要重写 hashCode()方法,以确保相等的对象具有相等的哈希码

以下是hashCode()方法的默认实现,来自Object类:

java 复制代码
public int hashCode() {
    return super.hashCode();
}

2.2 hashCode()重写(自定义类对象内容是否相等)

默认情况下,hashCode()方法返回的是对象的内部存储地址的哈希码,即对象的标识哈希码。然而,这种默认实现通常不满足我们对相等性的要求。

在重写hashCode()方法时,我们通常需要遵循以下几个原则:

graph LR A(重写hashCode方法遵循原则) B(相等的对象必须具有相等的哈希码) C(尽量使不相等的对象具有不同的哈希码 以提高哈希表的性能) D(哈希码的计算应基于对象的属性) A ---> B A ---> C A ---> D style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px
  1. 相等的对象必须具有相等的哈希码。这意味着如果equals()方法返回true,则两个对象的hashCode()方法应返回相同的值。

  2. 尽量使不相等的对象具有不同的哈希码,以提高哈希表的性能。尽量避免不相等的对象具有相同的哈希码,这会导致哈希冲突,影响查找效率。

  3. 哈希码的计算应基于对象的属性。通常可以使用对象的属性值来计算哈希码,例如字符串的哈希码可以使用其字符序列的哈希码计算得出。

下面是一个示例,演示了如何重写hashCode()方法:

java 复制代码
public class Person {

    private String name;
    private int age;

    // 构造函数、getter和setter方法等省略

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

注意 :重写hashCode()方法时,需要确保与equals()方法的逻辑一致。这样,当两个对象根据equals()方法比较相等时,它们的哈希码也应该相等。

正确实现hashCode()方法可以提高哈希表等数据结构的性能,并确保对象能够在集合类中正确存储和检索。

三、思考:两个对象的hashCode()相同,则 equals()是否也一定为true?

结论 :两个对象 equals() 相等,则它们的 hashCode() 必须相等,如果两个对象的hashCode()相同,则 equals()不一定为 true

3.1 扩展知识

Java中,关于hashCode()方法有一些常规协定和约定俗成的规则。这些规则有助于确保正确实现hashCode()方法,并使其在哈希表等数据结构中正常工作。

以下是hashCode()方法的常规协定 (非强制规定):

  • 一致性 :在对象没有发生属性变化的情况下,多次调用hashCode()方法应该始终返回相同的值。只有当对象的属性发生变化时,hashCode()方法的返回值才可以改变。

  • 相等性一致性 :如果两个对象根据equals()方法比较相等,那么它们的hashCode()方法必须返回相同的值。即使两个对象的属性发生变化,只要它们仍然相等,它们的哈希码必须保持一致。

  • 哈希码分布均匀性:不相等的对象应该具有不同的哈希码,以减少哈希冲突的可能性,提高哈希表等数据结构的性能。尽量使哈希码在整个可能的范围内均匀分布,以避免热点区域的聚集。

  • 性能考虑hashCode()方法的计算应该高效,尽量避免复杂的操作和耗时的计算。由于哈希码通常在哈希表等数据结构中频繁使用,性能较低的hashCode()方法可能会影响整体性能。

四、总结

Java中,equals()hashCode()方法是用于对象相等性比较和哈希表存储的重要方法。

这两个方法在一起使用,可以保证对象在哈希表等数据结构中正确存储和检索,并确保对象的相等性比较得到正确的结果。它们在集合类(如HashMapHashSet)中广泛使用,以提供高效的元素存储和查找功能。

希望本文对您有所帮助。如果有任何错误或建议,请随时指正和提出。

同时,如果您觉得这篇文章有价值,请考虑点赞和收藏。这将激励我进一步改进和创作更多有用的内容。

感谢您的支持和理解!

相关推荐
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈3 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水4 小时前
简洁之道 - React Hook Form
前端
2401_857622666 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
正小安6 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
2402_857589366 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没7 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch7 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光7 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js