Spring, CS 공부 내용 8

김정용·2025년 2월 1일

기술면접 공부

목록 보기
8/15
post-thumbnail

equals와 hashcode의 동시 재정의에 대해

알고리즘을 풀며 정렬문제를 풀다보면 hash 값을 사용하는 자료구조 문제에서 equals와 hashCode 메소드를 재정의 한 후 문제에 접근해야하는 경우가 더러 존재했다.

메소드를 재정의하는 경우는 알지만 왜 두개를 같이 재정의 해야할까?

hash 값을 사용하는 자료구조는 HashSet이나 HashMap 등이 있다.

이 자료구조들은 값을 저장할때, hash값 생성을 통해 값을 비교하고, 동일한 값은 중복 저장하지 않는다.

이 경우 hashCode의 메소드 반환값을 사용하게 되는데, hashCode의 반환 값이 일치하고 이후 equals 메소드의 반환값이 true 일때만 논리적으로 같은 객체라고 판단하게 된다.

예시

class EqualsHashCodeTest {

    @Test
    @DisplayName("equals만 정의하면 HashSet이 제대로 동작하지 않는다.")
    void test() {
        // 아래 2개는 같은 구독자
        Subscribe subscribe1 = new Subscribe("team.maeilmail@gmail.com", "backend");
        Subscribe subscribe2 = new Subscribe("team.maeilmail@gmail.com", "backend");
        HashSet<Subscribe> subscribes = new HashSet<>(List.of(subscribe1, subscribe2));

        // 결과는 1개여야하는데..? 2개가 나온다.
        System.out.println(subscribes.size());
    }

    class Subscribe {

        private final String email;
        private final String category;

        public Subscribe(String email, String category) {
            this.email = email;
            this.category = category;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Subscribe subscribe = (Subscribe) o;
            return Objects.equals(email, subscribe.email) && Objects.equals(category, subscribe.category);
        }
    }
}

예제 코드의 HashSet인 subscribes의 선언부를 보자.

Subscribe subscribe1 = new Subscribe("team.maeilmail@gmail.com", "backend");
        Subscribe subscribe2 = new Subscribe("team.maeilmail@gmail.com", "backend");

우리가 보기에 subscribe1 과 subscribe2는 같은 email 값과 같은 category 값을 가지고 생성되었기에 같은 값이라고 생각되고

HashSet<Subscribe> subscribes = new HashSet<>(List.of(subscribe1, subscribe2));

위와 같이 hashset에 넣게되면 중복된 값이 제거되어 System.out.println(subscribes.size());를 수행했을때, 1이 나온다고 예상할것이다.

하지만 출력결과 2라는 값이 나온다.


답은 Subscribe 클래스 내부에 함수 재정의 부분을 보면 알 수 있다.

equals 메소드는 override 를 통해 재정의를 잘 했지만 hashCode 메소드의 재정의 부분은 찾을 수 없다.

결과적으로 Object에서 사용되는 기본 hashCode 메소드를 사용하게 되고 이 기본 hashCode 메소드는 객체의 고유 주소로 같다 아니다를 판별하기에 객체마다 다른 값을 반환하여 다른객체로 판단된다. + equals도 주소를 기준으로 같다 아니다를 판별한다.

이러한 이유로 HashSet에서 다른 객체로 판단, 중복처리가 되지 않는다.


귀찮다면?

그렇다면 hashCode와 equals를 매번 동시에 재정의하게되는것에서 귀찮음을 느낄 수 있다...

@Lombok을 사용할 수 있는 환경에서는 대안이 존재한다 !

평소에도 getter, setter 등으로 수고를 덜어주는 롬복에서는 @EqualsAndHashCode 라는 어노테이션이 존재한다.

import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class Subscribe {
    private String email;
    private String category;
}

이런식으로 어노테이션을 사용하게되면 equals와 hashCode 메소드를 자동으로 생성하고 코드도 더 짧게 사용할 수 있다 !


참고

면접 질문 내용과 답변의 일부는 기술 면접 구독 서비스 - 매일메일 에 있다.
흥미로웠다면 구독해보는 것도 추천한다!

profile
누군가의 롤모델이 될 때까지😇

0개의 댓글