본문 바로가기
이직&취업/Java 기초 상식

자바의 equals()와 hashCode() 메서드는 어떻게 구현하나요?

by journeylabs 2025. 4. 9.
728x90
반응형

목차

    🔍 자바 객체 비교의 핵심! equals()와 hashCode() 완벽 이해와 구현 방법

    "왜 HashMap에서 내가 만든 객체가 제대로 비교되지 않을까?"
    "같은 값을 가졌는데 Set에 중복으로 들어가는 이유는 뭐지?"

    👉 그 해답은 바로 equals()와 hashCode() 메서드에 있습니다.
    이 두 메서드를 정확히 이해하고 구현하지 않으면, 버그 없이 자바 컬렉션을 쓰는 건 불가능에 가깝습니다.
    이 글에서 equals()와 hashCode()의 개념부터 실무 적용까지 완벽하게 정리해드릴게요.


    1. equals()hashCode()란? 주요 개념과 특징

    자바에서 equals()hashCode()Object 클래스에 정의된 메서드로, 객체의 동등성과 해시 기반 데이터 구조에서 중요한 역할을 합니다.

     

    equals()

    • 역할: 두 객체가 논리적으로 같은지 비교합니다.
    • 기본 구현: Object 클래스의 기본 equals()는 참조 동등성(==)만 확인합니다.
    • 특징: 개발자가 오버라이드하여 객체의 필드 값을 기반으로 동등성을 정의할 수 있습니다.

    hashCode()

    • 역할: 객체의 해시 값을 반환하며, HashMap이나 HashSet 같은 해시 기반 컬렉션에서 사용됩니다.
    • 기본 구현: 객체의 메모리 주소를 기반으로 한 고유 값을 반환합니다.
    • 특징: equals()로 같은 객체는 반드시 동일한 hashCode() 값을 가져야 합니다(계약 규칙).

    ✅ 규칙

    • equals()로 같으면 hashCode()도 같아야 함.
    • hashCode()가 다르면 equals()도 달라야 함(반대는 성립하지 않음).
    • 객체 상태가 변하지 않는 한 hashCode()는 항상 동일한 값을 반환해야 함.

    ✔ equals()의 규칙

    • 반사성: a.equals(a)는 항상 true
    • 대칭성: a.equals(b)가 true면 b.equals(a)도 true
    • 추이성: a.equals(b) && b.equals(c)이면 a.equals(c)도 true
    • 일관성: 비교 결과는 변하지 않아야 함 (불변 객체일 경우)
    • null 비교: a.equals(null)은 항상 false

    ✔ hashCode()의 규칙

    • 같은 객체는 항상 같은 hashCode()를 반환해야 함
    • 동일한 hashCode()를 가진다고 해서 반드시 equals()가 true는 아님
    • equals()가 true면, 반드시 hashCode()도 같아야 함

    쉽게 말해, equals()는 "내용이 같은지" 확인하고, hashCode()는 "빠른 검색을 위한 고유 번호"를 제공한다고 생각하면 됩니다.


    2. equals() hashCode() 구현 예시 및 코드 설명

    예제 1: 사용자(User) 클래스

     
    import java.util.Objects;
    
    public class User {
        private String id;
        private String name;
    
        public User(String id, String name) {
            this.id = id;
            this.name = name;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true; // 동일 객체
            if (o == null || getClass() != o.getClass()) return false; // 클래스 타입 확인
            User user = (User) o;
            return Objects.equals(id, user.id); // ID 기준으로 비교
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(id); // ID 기반으로 해시 생성
        }
    }
    • equals()에서는 클래스 타입 체크 후, 중요한 필드인 id만을 기준으로 비교합니다.
    • hashCode()도 id 필드를 기준으로 해시코드를 생성합니다.
    • Objects.equals()와 Objects.hash()를 사용하면 NPE 방지가독성 향상 효과가 있습니다.

    예제 2: Person 클래스에서 구현

    public 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 obj) {
            if (this == obj) return true; // 동일 참조 확인
            if (obj == null || getClass() != obj.getClass()) return false; // null 또는 클래스 다름
            Person other = (Person) obj; // 타입 캐스팅
            return age == other.age && name.equals(other.name); // 필드 비교
        }
    
        @Override
        public int hashCode() {
            int result = name.hashCode(); // name의 해시값
            result = 31 * result + age;   // age를 결합 (31은 소수로 충돌 최소화)
            return result;
        }
    
        public static void main(String[] args) {
            Person p1 = new Person("홍길동", 30);
            Person p2 = new Person("홍길동", 30);
            
            System.out.println("equals: " + p1.equals(p2)); // 출력: true
            System.out.println("hashCode: " + (p1.hashCode() == p2.hashCode())); // 출력: true
        }
    }

     

    equals():

    • this == obj: 동일한 참조면 바로 true.
    • null 체크와 클래스 확인으로 안전성 확보.
    • 필드(name, age)를 비교해 논리적 동등성 판단.

    hashCode():

    • name.hashCode()로 문자열의 해시 값을 가져옴.
    • 소수 31을 곱해 충돌 가능성을 줄이고, age를 추가해 고유성 강화.

    3. 실무에서 equals()hashCode() 활용 사례

    실무 예시 1: HashSet에서 중복 제거

    import java.util.HashSet;
    import java.util.Set;
    
    public class Main {
        public static void main(String[] args) {
            Set<User> users = new HashSet<>();
            users.add(new User("001", "Alice"));
            users.add(new User("001", "Alice")); // 같은 id, equals() 같음
    
            System.out.println("유저 수: " + users.size()); // 1 출력
        }
    }
    • equals()와 hashCode()를 올바르게 구현하지 않으면 위 예제에서 객체가 2개로 취급됩니다.
    • 실무에서는 회원 중복 등록, 권한 체크, 로그인 처리 등에서 이 문제가 자주 발생합니다.

    실무 예시 2: HashMap에서 키로 사용

    import java.util.HashMap;
    
    public class User {
        private int id;
        private String username;
    
        public User(int id, String username) {
            this.id = id;
            this.username = username;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            User other = (User) obj;
            return id == other.id && username.equals(other.username);
        }
    
        @Override
        public int hashCode() {
            return 31 * id + username.hashCode();
        }
    
        public static void main(String[] args) {
            HashMap<User, String> userMap = new HashMap<>();
            User user1 = new User(1, "admin");
            User user2 = new User(1, "admin");
    
            userMap.put(user1, "관리자");
            System.out.println(userMap.get(user2)); // 출력: 관리자
        }
    }
    • 활용 이유: equals()와 hashCode()가 제대로 구현되지 않으면 user1과 user2를 다른 키로 인식해 데이터가 중복되거나 조회되지 않을 수 있습니다.

    4. equals()hashCode()의 장단점: 주의사항

    ✅ 장점

    • 컬렉션 (HashMap, HashSet, Hashtable) 에서의 정확한 비교 가능
    • 비즈니스 로직에 맞춘 의미 있는 비교 구현 가능
    • 성능 향상 (효율적인 해시 분포)

    ❌ 단점 및 주의사항

    • equals()와 hashCode()의 불일치로 인해 버그 발생 가능성 높음
    • hashCode()를 과도하게 복잡하게 구현하면 해시 성능 저하
    • 모든 필드를 비교하면 성능 하락, 핵심 필드만 사용하는 것이 좋음
    • 구현 시 IDE 자동 생성 기능 사용을 추천 (Alt + Insert in IntelliJ)

    ✅ 결론: equals()와 hashCode()는 객체 비교의 필수 요소!

    자바에서 객체를 비교하거나 컬렉션을 사용할 때 equals()와 hashCode()는 무조건 구현되어야 하는 기본 중의 기본입니다. 
    특히 실무에서는 이 메서드들이 올바르게 정의되어야만 중복 체크, 검색, 삭제, 저장 등 모든 로직이 정확하게 동작합니다.이 글을 통해 두 메서드의 개념과 실무 적용법을 확실히 익히셨길 바랍니다. "객체 비교가 어렵다"는 고민은 이제 끝! 지금 코드를 작성하며 직접 적용해보세요.

    한 줄 요약: "equals()는 내용 비교, hashCode()는 위치 지정! 둘은 항상 함께 구현해야 한다."

    728x90
    반응형