[Why]
hashCode
를 재정의하지 않으면 해시를 이용한 컬랙션을 사용할 때 해시의 빠른 속도를 활용하지 못한다.
[When]
hashCode
를 재정의할 때
[How]
hashCode 일반 규약
equals
비교에 사용되는 정보가 변경되지 않았다면,hashCode
도 변하면 안 된다.(애플리케이션을 다시 실행한다면 이 값이 달라져도 상관 없음)equals
가 두 객체가 같다고 판단했다면, 두 객체의hashCode
는 똑같은 값을 반환한다.→ 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다.equals
가 두 객체를 다르다고 판단했더라도,hashCode
는 꼭 다를 필요는 없다.하지만, 다른 객체에 대해서는 다른 값을 반환해야 해시테이블의 성능이 좋아진다.
hashCode를 작성하는 요령
- int 변수인
result
를 선언한 후 값을 c로 초기화한다.- 이 때, c는 해당 객체의 첫번째 핵심 필드를 단계 2.1 방식으로 계산한 해시코드이다.
- 여기서 핵심 필드는
equals
비교에 사용되는 필드를 말한다.
- 해당 객체의 나머지 핵심 필드인 f 각각에 대해 다음 작업을 수행한다.
- 해당 필드의 해시코드 c 를 계산한다.
- 기본 타입 필드라면,
Type.hashCode(f)
를 수행한다. 여기서 Type은 해당 기본타입의 박싱 클래스다. - 참조 타입 필드면서, 이 클래스의
equals
메소드가 이 필드의equals
를 재귀적으로 호출하여 비교한다면, 이 필드의hashCode
를 재귀적으로 호출한다. - 필드가 배열이라면, 핵심 원소 각각을 별도 필드처럼 다룬다.모든 원소가 핵심 원소라면
Arrays.hashCode
를 사용한다.
- 기본 타입 필드라면,
- 단계 2.1에서 계산한 해시코드 c로
result
를 갱신한다.result
= 31 *result
+ c;
- 해당 필드의 해시코드 c 를 계산한다.
result
를 반환한다.
//예시
@Override
public int hashCode() {
int result = Integer.hashCode(areaCode);
result = 31 * result + Integer.hashCode(prefix);
result = 31 * result + Integer.hashCode(lineNum);
return result;
}
hashCode 캐싱과 지연 초기화
- 클래스가 불변이고 해시코드를 계산하는 비용이 크다면, 캐싱을 고려한다.
- 해시의 키로 사용되지 않는 경우라면
hashCode
가 처음 불릴 때 계산하는 지연 초기화- 해시코드 필드를 지연 초기화 하려면 그 클래스가 thread-safe가 되도록 동기화에 신경 쓰는 것이 좋다.
'programming > java' 카테고리의 다른 글
Item 13. clone 재정의는 주의해서 진행하라. (0) | 2023.04.18 |
---|---|
Item 12. toString을 항상 재정의하라. (0) | 2023.04.17 |
Item 10. equals 일반 규약을 지켜 재정의하라 (0) | 2023.03.28 |
Item 9. try-finally 보다는 try-with-resources를 사용하라 (0) | 2023.03.28 |
Item 8. finalizer와 cleaner의 사용을 피하라 (0) | 2023.03.24 |