해당 클래스의 인스턴스를 HashMap이나 HashSet 같은 컬렉션의 원소로 안전하게 사용하기 위해서는 hashCode를 재정의하여야 한다.
Map<PhoneNumber, String> m = new HashMap<>();
m.put(new PhoneNumber(707, 867, 5309), "제니");
m.get(new PhoneNumebr(707, 867, 5309)); // Return false
논리적 동치인 두 객체가 서로 다른 해시코드를 반환하여 두 번째 규약을 지키지 못하였다. 두 인스턴스를 같은 버킷에 담더라도
get메서드
는 null을 반환할 것이다.
int 변수 result를 선언한 후 값 c로 초기화한다.
해당 객체의 나머지 핵심 필드 f 각각에 대해 아래와 같이 수행한다.
(1) 기본 타입 필드라면, Type.hashCode(f)를 수행한다.
(2) 참조 타입 필드면서 이 클래스의 equasl 메서드가 이필드의 equals를 재귀적으로
호출해 비교한다면, hashCode를 재귀적으로 호출한다. (null -> 0으로 사용한다.)
(3) 필드가 배열이라면, 핵심 원소 각각을 별도 칠드처럼 다룬다. 모든 원소가 핵심 원소가
아니라면 상수(0)을 사용한다. 반대로, 모든 원소가 핵심 원소라면 Arrays.hashCode를 사용한다.
해시코드 c로 result를 갱신한다. result = 31 * result + c
result를 반환한다.
@Override
public int hashCode() {
int result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result= 31 * result + Short.hashCode(lineNum);
return result;
Objects 클래스는 임의의 개수만큼 객체를 받아 해시코드를 계산해주는 정적 메서드인 hash를 제공한다. 이 메서드를 활용하면 위의 코드를 한 줄로 줄여 작성할 수 있다.
@Override int hashCode(){
return Objects.hash(lineNum, prefix, areaCode);
}
클래스가 불변이고 해시코드를 계산하는 비용이 크다면, 캐싱하는 방식을 고려해야한다.. 지연초기화 전략을 고려해보자.
private int hashCode;
@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;
}
hashCode와 eqauls는 함께 재정의하여야 한다.