|

자바 equals와 hashCode는 왜 같이 생각해야 할까

자바 equals와 hashCode 차이를 설명하는 대표 이미지
객체 비교 계약이 깨지면 컬렉션에서 가장 먼저 이상한 동작이 나타난다

자바 equals와 hashCode는 따로 배우면 금방 아는 것 같지만, 같이 생각하지 않으면 HashMap이나 HashSet에서 금방 이상한 동작을 만납니다. 이 주제의 핵심은 문법보다 객체 비교 계약이 컬렉션 동작에 직접 연결된다는 점입니다.

이번 글에서는 Object의 equals/hashCode 계약을 먼저 짚고, 왜 둘을 같이 구현해야 하는지, 실무에서 어떤 문제가 생기는지 예시 중심으로 설명하겠습니다.

기준이 되는 계약은 Oracle Java SE 21의 Object.equals / hashCode 문서가 가장 분명합니다. 실무에서는 이 계약을 HashMap, HashSet 동작과 연결해서 이해하는 편이 훨씬 잘 남습니다.


equals는 무엇을 약속할까

Oracle 문서에 따르면 equals는 반사성, 대칭성, 추이성, 일관성을 만족하는 equivalence relation으로 동작해야 합니다. 쉽게 말해 같은 객체를 비교할 때는 일관된 기준이 있어야 한다는 뜻입니다.

즉 equals는 “두 객체를 같은 것으로 볼 것인가”를 정하는 계약입니다.


hashCode는 왜 따로 필요할까

hashCode는 해시 기반 컬렉션이 객체를 빠르게 찾기 위해 쓰는 값입니다. HashMap과 HashSet은 먼저 hashCode로 대략적인 위치를 찾고, 그 다음 필요하면 equals로 최종 비교합니다.

그래서 equals만 맞고 hashCode가 어긋나면, 논리적으로는 같은 객체인데 컬렉션 안에서는 다른 칸에 들어가 버릴 수 있습니다.


왜 둘을 같이 구현해야 할까

Oracle 문서는 두 객체가 equals로 같다면 hashCode도 같은 값을 돌려야 한다고 분명히 말합니다. 이 규칙이 깨지면 해시 컬렉션의 전제가 무너집니다.

class User {
    private final String email;

    User(String email) {
        this.email = email;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof User other)) return false;
        return email.equals(other.email);
    }

    // hashCode를 구현하지 않으면 문제가 생길 수 있다
}

이 상태에서 HashSet에 넣으면, email이 같은 두 User를 “같다”고 보면서도 해시 버킷은 다르게 처리될 수 있습니다.


HashMap에서 이상해지는 대표 장면

  • put은 했는데 containsKey가 기대와 다르게 동작한다
  • 중복이 없어야 할 Set에 논리적으로 같은 값이 들어간다
  • 키를 수정한 뒤 다시 찾지 못하는 문제가 생긴다

특히 가변 필드를 기준으로 equals/hashCode를 만들고, 그 필드가 나중에 바뀌면 더 위험합니다. 이미 들어간 버킷 위치와 논리적 비교 기준이 어긋날 수 있기 때문입니다.


실무에서 자주 하는 실수

  1. equals만 재정의하고 hashCode는 그대로 두는 것
  2. 가변 필드를 기준으로 hashCode를 만드는 것
  3. IDE가 생성한 코드를 의미 없이 복붙하고 기준을 점검하지 않는 것

핵심은 코드 생성 여부가 아니라, 무엇을 같은 객체로 볼 것인지 기준을 먼저 정하는 일입니다.

기본 설명은 이미 발행한 equals/hashCode 글과도 연결되고, API 설계 감각은 Java Optional 글과도 결이 맞닿습니다.


마무리

자바 equals와 hashCode를 같이 생각해야 하는 이유는 아주 단순합니다. equals는 논리적 동일성을 정하고, hashCode는 해시 컬렉션이 그 동일성을 효율적으로 다루게 돕기 때문입니다.

둘 중 하나만 맞으면 충분한 것이 아니라, 둘이 같은 기준을 공유해야 컬렉션에서도 기대한 대로 동작합니다.

함께보면 좋은 글