读《Effective Java》笔记 - 条目11

条目11:重写equals方法时应该总是重写hashCode方法

为什么要重写hashCode方法?

Java 的规范明确规定了hashCodeequals方法的关系,如果两个对象根据equals方法是相等的,那么它们的hashCode值必须相等。这个规定确保了在使用哈希相关的数据结构(如HashMapHashSet等)时,能够正确地工作。如果只重写了equals方法,而没有重写hashCode方法,就会导致程序在处理哈希集合时出现异常行为。

如果只重写equals会发生什么?

会出现问题的代码示例:

java 复制代码
import java.util.HashSet;
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 o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    // 注意:未重写hashCode方法

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + '}';
    }
}

public class Main {
    public static void main(String[] args) {
        HashSet<Person> people = new HashSet<>();

        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);

        // 添加两个逻辑上相等的对象
        people.add(person1);
        people.add(person2);

        // 打印集合
        System.out.println(people);
    }
}

运行结果:

java 复制代码
[Person{name='Alice', age=25}, Person{name='Alice', age=25}]

虽然person1person2在逻辑上是相等的(equals方法返回true),但由于hashCode未被重写,它们的哈希值是不同的(基于默认实现Object.hashCode)。因此,HashSet无法正确识别它们是同一个对象。

重写equalshashCode方法

java 复制代码
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 o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

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

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + '}';
    }
}

public class Main {
    public static void main(String[] args) {
        HashSet<Person> people = new HashSet<>();

        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);

        // 添加两个逻辑上相等的对象
        people.add(person1);
        people.add(person2);

        // 打印集合
        System.out.println(people);
    }
}

运行结果:

java 复制代码
[Person{name='Alice', age=25}]

hashCode的实现原则

  1. 一致性原则 。在同一程序执行期间,如果一个对象的状态没有改变,则其hashCode值必须始终一致。
  2. 相等性原则 。如果两个对象根据equals方法是相等的,那么它们的hashCode值必须相等。
  3. 高效性原则。将对象尽可能均匀地分布在哈希表中,以减少哈希冲突。

实现方式:

  • 使用Objects.hash()方法,这是最简单的方式。

  • 手动组合关键字段,确保分布均匀。

    java 复制代码
    @Override
    public int hashCode() {
        int result = 17; // 初始值
        result = 31 * result + (name == null ? 0 : name.hashCode());
        result = 31 * result + age;
        return result;
    }
相关推荐
狐凄42 分钟前
Python实例题:使用Pvthon3编写系列实用脚本
java·网络·python
董先生_ad986ad3 小时前
C# 中的 `lock` 关键字本质
开发语言·c#
Lxinccode3 小时前
Java查询数据库表信息导出Word-获取数据库实现[1]:KingbaseES
java·数据库·word·获取数据库信息·获取kingbasees信息
元亓亓亓3 小时前
Java后端开发day36--源码解析:HashMap
java·开发语言·数据结构
sd21315123 小时前
RabbitMQ 复习总结
java·rabbitmq
道剑剑非道3 小时前
QT 打包安装程序【windeployqt.exe】报错c000007d原因:Conda巨坑
开发语言·qt·conda
豆沙沙包?4 小时前
5.学习笔记-SpringMVC(P61-P70)
数据库·笔记·学习
小邓儿◑.◑4 小时前
C++武功秘籍 | 入门知识点
开发语言·c++
码银6 小时前
Java 集合:泛型、Set 集合及其实现类详解
java·开发语言
大G哥6 小时前
PHP标签+注释+html混写+变量
android·开发语言·前端·html·php