equals 与 hashCode

返回 面试 · 速查全集 Java常见面试题

Object 中两者默认基于对象地址;重写 equals 比较业务相等时,必须重写 hashCode


默认行为

// Object 默认
public boolean equals(Object obj) { return this == obj; }
public native int hashCode();  // 通常与地址相关
运算符/方法比较什么
==基本类型:值;引用类型:地址
equals默认同 ==;可重写为内容相等

契约(必须遵守)

  1. 自反性x.equals(x) 为 true
  2. 对称性x.equals(y)y.equals(x)
  3. 传递性x.equals(y)y.equals(z)x.equals(z)
  4. 一致性:多次调用结果不变(可变对象除外)
  5. 非空性x.equals(null) 为 false

hashCode 契约

  • 相等对象 hashCode 必须相同
  • hash 相同对象 可以不相等(哈希碰撞)

为什么 equals 相等必须 hashCode 相同

Map<Person, String> map = new HashMap<>();
Person p1 = new Person("张三", 18);
Person p2 = new Person("张三", 18);
 
// 若只重写 equals 不重写 hashCode:
map.put(p1, "A");
map.get(p2);  // ❌ 可能 null — p1/p2 equals 为 true 但 hash 不同,进不同桶

HashMap 先比 hash 定位桶,再比 equals;hash 不一致则永远找不到「相等」对象。

HashSet 底层是 HashMap,同样依赖这对方法去重。


推荐写法(IDE 生成)

public class Person {
    private final String name;
    private final int 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);
    }
}

要点

  • getClass() 而非 instanceof(除非需要子类对称性)
  • 参与 equals 的字段都应参与 hashCode
  • 不可变字段更适合做 key

常见面试题

Q:两个 hashCode 相同,equals 一定 true?
A:,碰撞时 hash 同而内容不同。

Q:equals 为 false,hashCode 可以相同吗?
A:可以(碰撞),但相等对象 hash 不能不同。

Q:为什么 String 适合做 HashMap 的 key?
A:不可变 → hash 不变;已正确实现 equals/hashCode。


相关笔记