equals로 재정의한 클래스에서 hashCode를 재정의하지 않으면 hashCode 일반규약을 어기게 되어 해당 클래스의 인스턴스를 HashMap이나 HashSet같은 컬렉션의 원소로 사용할 때 문제를 일으키게 됨 → equals를 재정의하려거든 hashCode도 재정의해라
<aside> 🏷️
///// Object 명세의 규약 ////
equals(Object)가 두 객체를 같다고 판단했다면 두 객체의 hashCode는 같은 값을 반환해야 한다.
</aside>
→ 논리적으로 같은 객체는 같은 해시코드를 반환해야 함
equals
를 사용해 물리적으로 다른 두 객체를 논리적으로 같다고 할 수 있지만 Object의 기본 hashCode
는 이를 다르다고 판단해 규약과 달리 서로 다른 값을 반환함
→ 논리적 동치인 두 객체가 서로 다른 해시코드를 반환하게 되어 문제가 생길 수 있음 (ex. HashMap 사용 시)
최악의 구현
@Override public int hashCode() { return 42; }
// 모든 객체에서 같은 값을 반환하므로 모든 객체가 해시테이블의 버킷 하나에 담기게 됨
// 평균 수행시간이 O(n)으로 느려짐
구현 방법
@Override public int hashCode(){
// 1. 해당 클래스의 박싱 클래스.hashCode로 result 초기화
int result = Short.hashCode(areaCode);
// 2. 필드들에 가지고 result 갱신
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
return result;
}
@Override public int hashCod(){
return Objects.hash(lineNum, prefix, areaCode);
}
해시코드 지연 초기화
private int hashCode;
@Override
public int hashCode() {
int result = hashCode; // 초기값 0을 가진다.
if(result == 0) {
int result = Integer.hashCode(areaCode);
result = 31 * result + Integer.hashCode(areaCode);
result = 31 * result + Integer.hashCode(areaCode);
hashCode = result;
}
return result;
}
hashCode
가 처음 불릴 때 계산하는 방식주의할 점
equals 비교에 사용되지 않은 필드는 반드시 제외한다.
hashCode 작성 시 성능을 위해 핵심 필드를 생략하지 말자.
→ 인스턴스들의 해시코드를 넓은 범위로 고르게 퍼트려주는 효과가 있을 수 있음!
hashCode가 반환하는 값의 생성규칙 API를 사용자에게 자세히 공표하지 말자.
→ 클라이언트가 API 문서에서 hashCode
의 계산 방식이 어떻게 되는지 알게 되면, 그 방식에 맞춰 자신의 hashCode
를 구현할 가능성이 높음. API의 변경이 있을 경우 클라이언트의 코드도 수정해야 할 수 있음