숏컷 코드
public final class User implements Comparable<User> {
private final long id;
private final String 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(...) |
HashMap/HashSet key로 사용 | equals와 hashCode를 함께 구현 |
| 클래스의 기본 정렬 순서 정의 | Comparable |
| 상황별 다른 정렬 기준 사용 | Comparator |
== vs equals: 참조 비교 vs 값 비교
==는 두 참조가 같은 객체를 가리키는지 보는 연산입니다. equals는 "이 두 객체를 같은 값으로 볼 것인가"를 정의하는 메서드입니다. 기본 Object.equals()는 ==와 동일하게 동작하므로, 값 객체에서는 반드시 재정의해야 합니다.
User u1 = new User(1L, "kim@example.com");
User u2 = new User(1L, "kim@example.com");
System.out.println(u1 == u2); // false (다른 객체)
System.out.println(u1.equals(u2)); // true (같은 값으로 정의)equals와 hashCode는 항상 함께
equals를 재정의했다면 반드시 hashCode도 함께 재정의해야 합니다. HashSet, HashMap 같은 hash 기반 컬렉션은 먼저 hashCode로 bucket을 찾고, 그 다음 equals로 동일성을 판정합니다. hashCode를 재정의하지 않으면 같은 값인데도 다른 bucket에 들어가 조회가 실패합니다.
// equals는 재정의했지만 hashCode를 재정의하지 않은 경우
Set<User> users = new HashSet<>();
users.add(new User(1L, "kim@example.com"));
// equals로는 같지만, hashCode가 다르면 조회 실패
boolean found = users.contains(new User(1L, "kim@example.com")); // false!Comparable: 기본 정렬 순서 정의
Comparable은 "자기 자신의 기본 정렬 기준"을 클래스 안에 넣는 방식입니다. TreeSet, Collections.sort() 같은 곳에서 자동으로 사용됩니다. 상황에 따라 정렬 기준이 바뀐다면 Comparator를 외부에서 주입하는 편이 더 유연합니다.
// Comparable — 클래스의 기본 순서
users.sort(null); // compareTo 기준 정렬
// Comparator — 화면/요구사항별 다른 기준
users.sort(Comparator.comparing(User::getEmail));
users.sort(Comparator.comparing(User::getCreatedAt).reversed());체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 같은 객체 참조인지 확인 | == |
| 값이 같은지 확인 | equals() 재정의 |
| hash 기반 컬렉션에서 올바르게 동작 | equals + hashCode 함께 재정의 |
| 기본 정렬 순서 정의 | Comparable<T> 구현 |
| 상황별 다른 정렬 전략 | Comparator 외부 주입 |
주의할 점
equals만 재정의하고 hashCode를 그대로 두면 HashSet·HashMap에서 조회가 실패합니다.
// ❌ equals만 재정의, hashCode 누락
public class Point {
int x, y;
@Override
public boolean equals(Object o) { ... } // hashCode 없음
}
Set<Point> set = new HashSet<>();
set.add(new Point(1, 2));
set.contains(new Point(1, 2)); // false! — hashCode가 다르면 다른 bucket
// ✅ equals + hashCode 항상 함께
@Override
public int hashCode() {
return Objects.hash(x, y);
}정렬 기준(compareTo)과 동등성 기준(equals)이 크게 어긋나면 TreeSet 같은 정렬 컬렉션에서 예상과 다른 중복 판정이 나올 수 있습니다.
// compareTo는 id만 비교, equals는 id+email 비교
// TreeSet은 compareTo 결과 0이면 같은 원소처럼 취급참고 링크
3 sources