Java객체지향

equals, hashCode, Comparable

객체 비교에서 참조 비교와 값 비교가 어떻게 다르고, equals/hashCode/Comparable 계약을 어떻게 맞춰야 하는지 정리합니다.

마지막 수정 2026년 3월 22일

기본 패턴

java
public final class User implements Comparable<User> {
    private final long id;
    private final String email;

    public User(long id, String email) {
        this.id = id;
        this.email = email;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(id, email);
    }

    @Override
    public int compareTo(User other) {
        return Long.compare(this.id, other.id);
    }
}

설명

  • ==는 두 참조가 같은 객체를 가리키는지 보는 연산이고, equals는 "이 두 객체를 같은 값으로 볼 것인가"를 정의하는 메서드입니다. 입문자가 가장 자주 헷갈리는 지점이 바로 이 차이입니다.
  • equals를 재정의했다면 보통 hashCode도 함께 재정의해야 합니다. 이유는 HashSet, HashMap처럼 hash 기반 컬렉션이 먼저 hashCode로 bucket을 고르고, 그 다음 equals로 실제 동일성을 판정하기 때문입니다.
  • Comparable은 "자기 자신의 기본 정렬 기준"을 클래스 안에 넣는 방식입니다. 예를 들어 id 오름차순을 기본 순서로 삼고 싶다면 compareTo를 구현할 수 있습니다.
  • equalscompareTo는 개념적으로 다른 계약입니다. equals는 동등성, compareTo는 정렬 순서입니다. 가능하면 둘이 크게 충돌하지 않도록 설계하는 편이 컬렉션에서 덜 놀랍게 동작합니다.
  • 실무에서는 record를 쓰면 값 기반 비교를 자동으로 얻을 수 있습니다. 하지만 어떤 필드를 동등성 기준으로 삼을지 더 세밀한 통제가 필요하면 직접 구현하는 편이 낫습니다.

빠른 정리

개념무엇을 결정하나자주 연결되는 곳
==같은 참조인가객체 identity 확인
equals같은 값으로 볼 것인가값 객체, 도메인 비교
hashCodehash 기반 컬렉션 bucketHashMap, HashSet
Comparable기본 정렬 순서Collections.sort, TreeSet
Comparator외부 정렬 기준다양한 정렬 전략

주의할 점

equals만 재정의하고 hashCode를 그대로 두면 HashSet이나 HashMap에서 같은 값인데도 중복으로 들어가거나 조회가 실패하는 일이 생길 수 있습니다. 두 메서드는 항상 함께 설계하는 습관이 중요합니다.

참고 링크

3 sources