[effective java] 3장 item11 - equals를 재정의하려거든 hashCode도 재정의하라

2019-02-11

equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다.

hashCode를 잘못 재정의 했을때 크게 문제가 되는 조항은 아래 두번째 조항. 즉, 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다.

Object 명세에서 발췌한 규약

  • 애플리케이션이 실행되는 동안 한 객체애 대헌 hashCode 메서드는 몇번을 호출해도 일관되게 항상 같은 값을 반환해야 한다.
  • equals(Object)가 두 객체를 같다고 판단했다면 두 객체의 hashCode는 똑같은 값을 반환해야 한다.
  • 두 객체가 다르다고 판단했더라도 서로 다른 hashCode를 반환할 필요는 없다. but, 좋은 해시 함수는 서로 다른 인스턴스에 다른 해시코드를 반환한다. 그게 성능도 더 좋다.

좋은 hashCode를 작성하는 간단한 요령

  1. int 변수 result를 선언한 후 값 c로 초기화한다.
  2. 해당 객체의 나머지 핵심 필드 f 각각에 대해 다음 작업을 수행한다.

    a. 해당 필드의 해시코드 c를 계산한다.

    b. 위에서 계산한 해시코드 c로 result를 갱신한다. result = 31* result + c;

  3. result를 반환한다.

다 구현했다면 이 메서드가 동치인 인스턴스에 대해 똑같은 해시 코드를 반환할지 자문하고 테스트를 해보자.(AutoValue 굳)

파생필드는 해시코드 계산에서 제외OK equals 비교에 사용되지 않은 필드는 반드시 제외해야 한다.

클래스가 불변이고 해시코드를 계산하는 비용이 크다면 캐싱하는 방식을 고려해야 한다.

해시의 키로 사용되지 않는 경우라면 hashCode가 처음 불릴 때 계산하는 지연 초기화 전략도 있다.(스레드 안전성 고려)

성능을 높인답시고 해시코드를 계산할 때 핵심 필드를 생략해서는 안된다.

hashCode가 반환하는 값의 생성 규칙을 API사용자에게 자세히 공표하지 말자. 그래야 클라이언트가 이 값에 의지하지 않게 되고 추후에 계산 방식을 바꿀 수도 있다.