读《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;
    }
相关推荐
一只小bit28 分钟前
C++之初识模版
开发语言·c++
P7进阶路1 小时前
Tomcat异常日志中文乱码怎么解决
java·tomcat·firefox
王磊鑫1 小时前
C语言小项目——通讯录
c语言·开发语言
钢铁男儿1 小时前
C# 委托和事件(事件)
开发语言·c#
Ai 编码助手1 小时前
在 Go 语言中如何高效地处理集合
开发语言·后端·golang
小丁爱养花1 小时前
Spring MVC:HTTP 请求的参数传递2.0
java·后端·spring
喜-喜1 小时前
C# HTTP/HTTPS 请求测试小工具
开发语言·http·c#
ℳ₯㎕ddzོꦿ࿐2 小时前
解决Python 在 Flask 开发模式下定时任务启动两次的问题
开发语言·python·flask
CodeClimb2 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
一水鉴天2 小时前
为AI聊天工具添加一个知识系统 之63 详细设计 之4:AI操作系统 之2 智能合约
开发语言·人工智能·python