brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Mar 05. 2018

모든 객체에 공통적인 메서드

#09 hashCode 메서드도 항상 같이 오버라이드 하자

Effective Java - 모든 객체에 공통적인 메서드


#09 equals 메서드를 오버 라이드 할 때는 hashCode 메서드도 항상 같이 오버라이드 하자


제목 그대로 equals 메서드를 오버 라이드 할 때는 hashCode 메서드도 항상 같이 오버라이드 해야 합니다.

그렇지 않으면, hashCode 메서드의 보편적 계약을 위반하게 되므로, HashMap/HashSet/Hashtable과 같은 hash 기반의 Collection들을 이용할 때 문제가 생길 수 있습니다.




hashCode 보편적 계약


같은 객체를 여러 번 호출하더라도 hashCode 메서드는 항상 같은 정수를 반환해야 합니다.
단, Application이 다시 실행되면 이 정수 값이 변할 수 도 있습니다.

equals(Object) 메서드 호출 결과가 true이면, hashCode 호출 결과도 같은 정수 값이 나와야 합니다.

equals(Object) 메서드 호출 결과가 false이더라도, hashCode 호출 결과가 반드시 다른 정수 값이 나올 필요는 없습니다.
그러나 서로 다른 hashCode 값이 나오면 hash collection들의 성능이 향상될 수 있습니다.


어찌 보면 당연한 내용들인데, 동일한 객체는 동일한 hash 코드 값을 가져야 하는 두 번째 보편적 계약 사항을 조심해야 합니다.

일반적인 equals 메서드에서는 두 개의 인스턴스가 논리적으로 동일할 수 있지만, hashCode 메서드에서는 논리적으로 동일한지 알 수 없습니다.


아래 코드는 equals 메서드만 오버라이드 한 PhoneNumber 클래스입니다.

#01 PhoneNumber class

이를 아래와 같이 m.get(new PhoneNumber(101, 243, 4321))을 호출하면 "Seunghyun"이 반환될 거 같지만 실제로는 null이 반환됩니다.


Map에 put 한 new PhoneNumber(101, 243, 4321)와 get 하는 new PhoneNumber(101, 243, 4321) 두 인스턴스가 동일한 값을 가지고 있지만 PhoneNumber 클래스에서 hashCode 메서드를 오버라이드 하지 않아서 두 인스턴스가 서로 다른 hashCode 값을 반환합니다. (hashCode 보편적 계약 위반)


HashMap에서는 hashCode와 연관된 최적화 코드를 가지고 있기 때문에, hashCode 값이 일치하지 않으면 객체의 동일 여부를 확인하지 못합니다.


#02 return null




hashCode 메서드 작성


equals(Object) 메서드 호출 결과가 false인 두 객체들의 hashCode 값이 최대한 다르게 나오도록 hashCode 메서드를 오버라이드 해야 합니다.


아래와 같이 구현할 수 있습니다.

#03 hashCode 메서드


1. 우선 0이 아닌 상수 값을 int result 변수에 선언합니다. ex) 17

2. 각 필드(f)에 대해 아래와 같이 hash code int 값을 구합니다.

이때, equals 메서드에서 비교하는 필드만을 이용해 hashCode를 작성해야 합니다.

boolean 이면, (f ? 1 : 0)

byte, char, short, int 이면, (int) f

long 이면, (int) (f ^ (f >>> 32))

float 이면, Float.floatToIntBits(f)

double 이면, Double.doubleToLongBits(f)를 통해 long 값을 얻은 후, 다시 (int) (f ^ f >>>32))

참조형 이면, 객체의 hashCode 메서드 호출 (null 이면 0)

3. hash code int 값을 아래와 같이 result에 더합니다.

result = 31 * result + (int) hash code

4. result를 반환합니다.


PhoneNumber 클래스의 hashCode 메서드는 아래와 같이 구현할 수 있습니다.

#04 PhoneNumber hashCode


만약 불변 객체이면서 hashCode 연산 비용이 커서 매번 계산하지 않고 내부에 캐시 형태로 저장해야 한다면, 아래와 같이 늦 초기화(lazy initialization)를 할 수 있습니다.


#05 Lazy initialization




hashCode를 어떻게 구현하느냐에 따라 프로그램의 성능도 향상할 수 있습니다.

위에서 이용한 방법도 좋지만 최선의 방법이 아니기에 이 부분에 대해서는 오라클에게 맡깁시다ㅠ


매거진의 이전글 모든 객체에 공통적인 메서드
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari