读《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;
    }
相关推荐
蓝婷儿5 分钟前
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
开发语言·python·学习
渣渣盟21 分钟前
基于Scala实现Flink的三种基本时间窗口操作
开发语言·flink·scala
网安INF24 分钟前
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
java·web安全·网络安全·flink·漏洞
一叶知秋哈24 分钟前
Java应用Flink CDC监听MySQL数据变动内容输出到控制台
java·mysql·flink
jackson凌30 分钟前
【Java学习笔记】SringBuffer类(重点)
java·笔记·学习
sclibingqing35 分钟前
SpringBoot项目接口集中测试方法及实现
java·spring boot·后端
程序员JerrySUN39 分钟前
全面理解 Linux 内核性能问题:分类、实战与调优策略
java·linux·运维·服务器·单片机
糯米导航43 分钟前
Java毕业设计:办公自动化系统的设计与实现
java·开发语言·课程设计
糯米导航1 小时前
Java毕业设计:WML信息查询与后端信息发布系统开发
java·开发语言·课程设计
米粉03051 小时前
深入剖析Nginx:从入门到高并发架构实战
java·运维·nginx·架构