Item11
Item11
equals를 재정의하려거든 hashCode도 재정의하라
equals
메서드는 물리적으로 서로 다른 객체 역시 논리적으로는 같다고 판단할 수 있음equals
메서드가 두 객체를 같다고 판단한 경우, 두 객체의hashCode
반환 값 역시 동일해야 함
문제가 생기는 예시
- 다음 예제에서 Point 객체는 equals만 정의되어 있다고 가정
- 이 때 처음 생성한 Point 객체와 두 번째 생성한 Point 객체는 논리적으로 동일하긴 함
- 그러나 hashcode가 다르므로, HashMap에서 두 객체를 비교하지조차 않음
- 애초에 다른 해시 bucket에 있을 수도 있음
hashCode
메소드만 재정의해주면 쉽게 해결되는 문제
Map<Point, Integer> m = new HashMap<>();
m.put(new Point(3, 4), 5)
m.get(new Point(3, 4)) // 이 값은 null임
hashCode 재정의 방법
- hashCode를 재정의할때는, equals 비교에 사용되는 필드만 이용
- 기본 타입 필드의 경우에는
Type.hashCode(f)
를 이용해서 계산함 - 참조 타입 필드의 경우에는
hashcode
를 재귀적으로 호출 - 배열 타입 필드의 경우에는 각 배열의 원소를 하나의 필드처럼 고려
- 이렇게 구한 각 필드별 hashcode 값을 이용해, result = 31 * result + c 형태로 모두 더함
x * 31
은x << 5 - x
와 동일하므로, 연산에 이점 존재
- 기본 타입 필드의 경우에는
- 이렇게 직접 재정의하지 않고,
Object.hash
메서드를 사용하는 방법도 존재- 그러나 이 방법은 살짝 느릴 수 있으므로, 성능이 중요하지 않은 환경에서만 사용
주의점
- 모든 객체에 대해 hashcode를 상수로 반환하는 방법도 존재하나, 당연하게도 비효율적
- 모든 객체가 해시테이블의 한 버킷 안에 담기기 때문
- 해시 테이블의 시간 복잡도가 O(N)이 됨
- 성능을 위해 계산 시 특정 필드를 생략하는 방법은 절대 금지됨!
- 또한 hashCode 생성 규칙은 사용자에게 공표하지 않는 것이 추천됨
- 사용자가 이 생성 규칙을 사용하지 않게 해야 함
- 나중에 생성 규칙 변경하기도 쉬움
References
- 조슈아 블로크 - Effective Java 3/E