핵심 정리: hashCode 규약
equals 비교에 사용하는 정보가 변경되지 않았다면 hashCode는 매번 같은 값을 리턴해야 한다. (변경되거나, 애플리케이션을 다시 실행했다면 달라질 수 있다.)
두 객체에 대한 equals가 같다면, hashCode의 값도 같아야 한다.
두 객체에 대한 equals가 다르더라도, hashCode의 값은 같을 수 있지만 해시 테 이블 성능을 고려해 다른 값을 리턴하는 것이 좋다
hashcode 를 사용할 때 equals 를 사용했던 필드를 사용해야 한다.
hashmap 의 경우 hash value 값을 기준으로 가져오는데 null 값일 때는 해시 코드 값을 구해서 그 해시 코드 값에 대한 버킷을 찾아서 그 버킷에 넣어준다.
public class HashMapTest {
public static void main(String[] args) {
Map<PhoneNumber, String> map = new HashMap<>();
PhoneNumber number1 = new PhoneNumber(123, 456, 7890);
PhoneNumber number2 = new PhoneNumber(456, 789, 1111);
// TODO 같은 인스턴스인데 다른 hashCode
// 다른 인스턴스인데 같은 hashCode를 쓴다면? 같을 수 도 있다.
// -> hash collision
System.out.println(number1.equals(number2));
System.out.println(number1.hashCode());
System.out.println(number2.hashCode());
map.put(number1, "keesun");
map.put(number2, "whiteship");
String s = map.get(new PhoneNumber(123, 456, 7890)); // return null
String s = map.get(number2);
System.out.println(s);
}
}
핵심 정리: hashCode 구현 방법
사실상 ide 의 필드를 활용한 hashcode 를 override 를 실행하면 된다.
hash 알고리즘은 외부에 노출할 필요가 없다.
캐싱을 사용해 불변 클래스의 해시 코드 계산 비용을 줄일 수 있다
좋은 방법으로는 EqualsAndhashCode 어노테이션을 활용하자.
return Objects.hash(field. ... ); -> return int
// 해시코드를 지연 초기화하는 hashCode 메서드 - 스레드 안정성까지 고려해야 한다. (71쪽)
private volatile int hashCode; // 자동으로 0으로 초기화된다.
@Override public int hashCode() {
if (this.hashCode != 0) {
return hashCode;
}
synchronized (this) {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
this.hashCode = result;
}
return result;
}
}