[Effective Java 3/e] 아이템 11 - equals를 재정의하려거든 hashCode도 재정의하라
아이템 11 - equals를 재정의하려거든 hashCode도 재정의하라
- equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다.
- 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다.
최악의(하지만 적법한) hashCode 구현 - 사용 금지
@Override public int hashCode() { return 42; }
- 모든 객체에 똑같은 값을 내어주므로 모든 객체가 해시 테이블의 버킷 하나에 담겨 연결 리스트처럼 동작
- O(1) -> O(n) 으로 느려짐
좋은 hashCode 작성하는 방법
- int 변수 result를 선언한 후 값 c로 초기화한다.
- c는 해당 객체의 첫번째 핵심 필드를 해시코드로 계산 한 것
- 해당 객체의 나머지 핵심 필드 f 가각에 대해 다음 작업을 수행한다.
- 해당 필드의 해시코드 c를 계산한다.
- 계산한 해시코드 c로 result를 갱신한다.
- ex) result = 31 * result + c;
- 곱할 숫자가 31인 이유
- 홀수이면서 소수(prime)이기 때문
- 소수를 곱하면 곱셈을 시프트 연산과 빨셈으로 대체해 최적화 할 수 있다.
- 31 * i = (i « 5) - i
- 곱할 숫자가 31인 이유
- result를 반환한다.
전형적인 hashCode 메서드
@Override public int hashCode() {
int result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
return result;
}
한 줄짜리 hashCode 메서드 - 성능이 살짝 아쉽다
@Override public int hashCode() {
return Objects.hash(lineNum, prefix, areaCode);
}
- 속도가 느리다.
- 입력 인수를 담기 위한 배열이 만들어지고, 입력 중 기본 타입이 있다면 박싱과 언박싱도 거쳐야 하기 때문
- 성능에 민감하지 않을 때 사용 가능
해시코드를 지연 초기화하는 hashCode 메서드 - 스레드 안정성까지 고려해야 한다
private int hashCode; // 자동으로 0으로 초기화된다.
@Override public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
hashCode = result;
}
return result;
}
- 클래스가 불변이고 해시코드 계산 비용이 크다면 캐싱하는 방법을 고려해야 한다.