핵심 정리: 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;
        }
    }
  1. 핵심 필드 하나의 값의 해쉬값을 계산해서 result 값을 초기화 한다.
  2. 기본 타입은 Type.hashCode 참조 타입은 해당 필드의 hashCode 배열은 모든 원소를 재귀적으로 위의 로직을 적용하거나, Arrays.hashCode result = 31 * result + 해당 필드의 hashCode 계산값
  3. result를 리턴한다