[이펙티브 자바.아이템 11] equals 를 재정의하거든 hashcode 도 재정의하라

박상준·2024년 5월 30일
0

이펙티브 자바

목록 보기
9/19
post-custom-banner

hashcode 재정의의 필요성

  • equals 를 재정의한 클래스는 반드시 hashCode 도 재정의해야한다.
  • Object 의 일반 규약을 준수하기 위함이며, 이를 지키지 않으면 HashMap 이나 HashSet 같은 컬렉션에서 문제를 일으킨다.
    • 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다.

HashMap 과 hashcode 의 연관성

public class Main {
    public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<>();
        m.put(new PhoneNumber("010", 1234, 5678), "박상준");

        System.out.println(m.get(new PhoneNumber("010", 1234, 5678))); // null 이 출력됨.
    }

    static class PhoneNumber {
        private String key;

        private int num;

        private int num2;

        public PhoneNumber(String key, int num, int num2) {
            this.key = key;
            this.num = num;
            this.num2 = num2;
        }
    }
}
  • PhoneNumber 클래스는 hashcode 를 재정의하지 않았기에 논리적 동치인 2 객체가 서로 다른 해시코드 반환하여 규약을 지키지 못함

    규약

    • equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을
      반환해야 한다

잘못된 hashcode() 의 구현

@Override
public int hashCode() {
    return 42;
}
  • 모든 객체에 동일한 해시코드를 반환하여 해시 테이블의 성능을 O(n) 으로 떨어뜨리는 hashcode() 이다.
  • 내부적으로 해시충돌이 무한정으로 발생하는 상황을 말한다.

좋온 hashcode() 메서드 작성 요령

result = 31 * result + c;
  • int 변수 result 를 선언하고 첫 번째 핵심 필드로 초기화한다.
  • 나머지 핵심 필드 각각에 대해 필드의 hashcode 를 계산하고 갱신

전형적인 hashcode() 메서드의 예시

    static class PhoneNumber {
        private String key;

        private int num;

        private int num2;

        public PhoneNumber(String key, int num, int num2) {
            this.key = key;
            this.num = num;
            this.num2 = num2;
        }

        @Override
        public final boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof PhoneNumber that)) return false;

            return num == that.num && num2 == that.num2 && Objects.equals(key, that.key);
        }

        @Override
        public int hashCode() {
            int result = Objects.hashCode(key);
            result = 31 * result + num;
            result = 31 * result + num2;
            return result;
        }
    }
  • hashCode 의 경우 key 의 경우 String 이기에, 별도의 해싱을 수행,
  • 숫자 필드의 경우 그냥 숫자를 더해주기만해도 논리적인 동치성을 유지할 수 있음.

해시코드를 미리 캐싱해놓는 방법

private int cachedHashCode;

---

@Override
  public int hashCode() {
      int result = cachedHashCode;

      if(result == 0) {
          result = Objects.hashCode(key);
          result = 31 * result + num;
          result = 31 * result + num2;
          cachedHashCode = result;
      }

      return result;
  }
  • 그냥 인스턴스 자체에 별도의 캐싱된 해시값을 가지면 된다.
  • 근데 솔직히 굳이굳이 이다.
profile
이전 블로그 : https://oth3410.tistory.com/
post-custom-banner

0개의 댓글