hashCode와 equals에 대한 Object 명세
- equals(Object)가 두 객체를 같다고 판단하면 두 객체의 hashCode는 같은 값을 반환해야 한다.
- equals(Object)가 두 객체를 다르다고 판단해도, 두 객체의 hashCode가 서로 다른 값을 반환할 필요는 없다. 단, 다른 객체에 대해서는 다른 값을 반환해야 해시 테이블의 성능이 좋아진다.
hashCode를 재정의하지 않을 때 생기는 문제
Item 10에서 보았듯이 equals는 물리적으로 다른 두 객체를 논리적으로 같다고 할 수 있다. 그러나 Object에 정의된 기본 hashCode 메서드는 물리적으로 다르나, 논리적으로는 같은 이 객체를 전혀 다르다고 판단해 다른 값을 반환한다.
이전 Item 예시인 PhoneNumber 클래스를 생각해보면, 이 클래스는 hashCode를 재정의하지 않았기 때문에 논리적으로 같은 객체에 다른 해시코드를 반환한다. 예를 들어, 아래 코드에서 m.get()은 "제니"를 반환하지 않는다.
Map<PhoneNumber, String> m = new Hashmap<>();
m.put(new PhoneNumber(707, 867, 5309), "제니");
// 아래 값은 "제니"를 반환하지 않는다.
m.get(new PhoneNumber(707, 867, 5309));
좋은 해시 함수
좋은 해시 함수는 서로 다른 인스턴스에 다른 해시코드를 반환한다.
hashCode 작성 순서
- 결과값을 c로 초기화한다. (c는 객체의 첫 번째 핵심 필드를 2(1) 방식으로 계산한 해시 코드이다.)
- 해당 객체의 나머지 핵심 필드 각각에 대해 다음 작업을 수행한다.
- 핵심 필드의 해시코드를 계산한다.
- 기본 타입이면, Type.hashCode(f)를 수행한다.
- 참조 타입이면서 클래스의 equals가 필드의 equals를 재귀적으로 호출한다면, 이 필드의 hashCode를 재귀적으로 호출한다.
- 배열이라면, 배열 내 핵심 원소 각각을 별도의 필드처럼 다뤄 계산한다. 배열 해시 코드를 계산하면 2(2) 방법대로 갱신한다. 모든 원소가 핵심 원소라면 Arrays.hashCode를 사용한다.
- 해시 코드로 reault를 갱신한다.
- 핵심 필드의 해시코드를 계산한다.
- result를 반환한다.
전형적인 hashCode 메서드
아래 메서드는 인스턴스의 핵심 필드만을 사용해 간단하게 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 작성 시 주의할 점
- 동치인 인스턴스에 똑같은 해시 코드를 반환해야 한다.
- AutoValue로 생성한 것이 아니면 단위 테스트를 작성해 이 사실을 검증해야 한다.
- 파생 필드는 해시코드 계산에서 제외해도 된다.
- equals 비교에 사용되지 않는 필드는 반드시 제외해야 한다.
- Object에서 제공하는 hash 메서드를 사용할 수 있다.
- 속도가 느리므로 성능에 민감하지 않을 때만 사용해야 한다.
- 성능을 높이기 위해 핵심 필드를 생략하면 안 된다.
- 해시 테이블 속도가 느려질 수 있다.
- hashCode가 반환하는 값의 생성 규칙을 API 사용자에게 자세히 공표하지 않아야 한다.
클라이언트가 값에 의존하지 않아야 추후에 계산 방식을 바꿀 수 있다.
'Java > 내맘대로 Effective Java' 카테고리의 다른 글
[Item 13] clone 재정의는 주의해서 진행해라 (0) | 2022.03.17 |
---|---|
[Item 12] toString을 항상 재정의하라 (0) | 2022.03.16 |
[Item 10] equals는 일반 규약을 지켜 재정의하라 (0) | 2022.03.11 |
[Item 9] try-finally보다는 try-with-resources를 사용하라 (0) | 2022.03.11 |
[Item 8] finalizer와 cleaner 사용을 피하라 (0) | 2022.03.10 |