모든 객체에 공통적인 메소드 - 9. equals 메소드를 오버라이드 할 때는 hashCode 메소드도 항상 같이 오버라이드 하자.
Programs/Java 2012. 11. 5. 10:45Effect Java 제2판(Joshua Bloch / Addison Wesley) 을 공부하고 정리한 내용 입니다.
9. equals메소드를 오버라이드 할 때는 hashCode 메소드도 항상 같이 오버라이드 하자.
Object.hashcode 메소드 명세서에서 가져 온 계약 사항은 다음과 같다.
- 애플리케이션 실행 중에 같은 객체에 대해 한 번 이상 호출되더라도 hashCode 메소드는 같은 정수를 일관성 있게 반환해야 한다(equals 메소드에서 비교하는 객체의 값이 변경되지 않는다면).
단, 이 정수 값은 애플리케이션이 매번 다시 실행될 때마다 일관되게 같을 필요는 없다. - equals(Object) 메소드 호출 결과 두 객체가 동일하다면, 두 객체 각각에 대해 hashCode 메소드를 호출했을 때 같은 정수 값이 나와야 한다.
- equals(Object) 메소드 호출 결과 두 객체가 다르다고 해서 두 객체 각각에 대해 hashCode메소드를 호출했을 때 반드시 다른 정수 값이 나올 필요는 없다. 그러나 같지 않은 객체들에 대해 hashCode 메소드에서 서로 다른 정수 값을 반환하면, 이 메소드를 사용하는 해시 컬렉션들(HashMap, HashSet, Hashtable등)의 성능을 향상시킬 수 있음을 알아야 한다.
hashCode의 오버라이딩에 실패했을 때 위배되는 주요 조항은 두 번째 것으로써, 동일한 객체들은 같은 해시 코드 값을 가져야 한다는 것이다.
// 최악의 hashCode 메소드 - 절대 이렇게 사용하지 말 것! @Override public int hashCode() { return 42; }
HashCode 를 만드는 간단한 방법
- 예를 들어, 17과 같이 0이 아닌 어떤 상수 값을 result라는 int 변수에 저장한다.
- 우리 객체의 각 주요 필드(equals 메소드에서 비교하는) f에 대해 다음을 수행한다.
- 각 필드에 대한 int 타입의 해시 코드 c를 다음과 같이 산출한다.
- 필드 f가 boolean 타입이면, (f ? 1 : 0)
- 필드 f가 byte, char, short, int 타입이면 (int)f
- 필드 f가 long 타입이면, (int)(f ^ (f >>> 32)).
- 필드 f가 float 타입이면 Float.floatToIntBits(f)
- 필드 f가 double 타입이면 Double.doubleToLongBits(f)를 실행한 후 반환된 long 타입의 값을 앞의 III 처럼 처리하여 해시 코드 값을 구한다.
- 필드 f가 객체 참조일 경우는, 현재(equals 메소드가 호출된) 객체의 equals 메소드에서 그 필드를 비교하기 위해 f가 참조하는 객체의 equals 메소드를 재귀적으로 호출한다. 그러면 그 객체의 필드에 대해 hashCode 메소드도 재귀적으로 자동 호출된다. 만일 더 복잡한 비교(객체의 여러 필드를 비교하거나, 필드의 복잡한 연산과 같은)가 필요하다면, 필드의 "표준 형식"을 만들어 처리하고 그 표준 형식에 대해 hashCode 메소드를 호출한다. 만일 필드 f의 값이 null이면, 0을 반환한다(또는 다른 어떤 상수 값도 가능하지만 관례적으로 0을 사용한다.)
- 필드 f가 배열이라면 배열의 각 요소를 별개의 필드처럼 처리한다. 즉, 위의 규칙들을 적용하여 처리를 해야 할 요소 각각의 해시 코드 값을 산출한다. 그리고 바로 밑의 2.2 수식을 적용하여 그값들의 합을 구한다. 만일 배열 필드의 모든 요소를 처리해야 한다면 자바 1.5 버전에 새로 추가되어 오버로딩 된 Arrays.hashCode 메소드들 중 하나를 사용할 수 있다.
- 앞의 2.1 단계에서 구한 코드 c를 result에 합계한다.
result = 31 * result + c; - result 를 반환한다.
- hashCode 메소드 작성이 끝나면 동일한 인스턴스들이 같은 값의 해시 코드를 갖는지 검토하자. 그리고 단위 테스트를 작성하여 잘 실행되는지 검증하자.
// 어떤 클래스의 세 개의 중요 필드가 있고, 모두 short 타입 일 경우 @Override public int hashCode() { int result = 17; result = 31 * result + areaCode; result = 31 * result + prefix; result = 31 * result + lineNumber; return result; }
'Programs > Java' 카테고리의 다른 글
The Java Native Interface (JNI) 관련 (0) | 2014.04.21 |
---|---|
모든 객체에 공통적인 메소드 - 10. toString 메소드는 항상 오버라이드 하자. (1) | 2012.11.05 |
모든 객체에 공통적인 메소드 - 8. equals 메소드를 오버라이딩 할 때는 보편적 계약을 따르자. (0) | 2012.11.02 |
객체의 생성과 소멸 - 7. 파이널라이저(finalizer)의 사용을 피하자 (1) | 2012.11.01 |
객체의 생성과 소멸 - 6. 쓸모 없는 객체 참조를 제거하자. (0) | 2012.11.01 |