读《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;
    }
相关推荐
laplace01237 小时前
Java八股—MySQL
java·mysql·oracle
zhangyao9403308 小时前
关于js导入Excel时,Excel的(年/月/日)日期是五位数字的问题。以及对Excel日期存在的错误的分析和处理。
开发语言·javascript·excel
熙客8 小时前
TiDB:分布式关系型数据库
java·数据库·分布式·tidb
骑驴看星星a8 小时前
【Three.js--manual script】4.光照
android·开发语言·javascript
你想考研啊9 小时前
linux安装jdk和tomcat和并自启动
java·linux·tomcat
星释9 小时前
Rust 练习册 :Leap与日期计算
开发语言·后端·rust
悟能不能悟11 小时前
java的java.sql.Date和java.util.Date的区别,应该怎么使用
java·开发语言
循环过三天11 小时前
3.4、Python-集合
开发语言·笔记·python·学习·算法
高山上有一只小老虎12 小时前
java 正则表达式大全
java·正则表达式
昌sit!13 小时前
Linux系统性基础学习笔记
linux·笔记·学习