读《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;
    }
相关推荐
小羊没烦恼!2 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
一隅论数智2 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
俊昭喜喜里2 天前
java中的继承和多态的区别
java
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕2 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码2 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖2 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时2 天前
01-06-A-JVM排查实战详解
java·jvm
伞伞悦读2 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
vipxieliang2 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot