(Effective Java) equals를 재정의할 때는 반드시 hashCode도 재정의 하라
* equals를 재정의할 때는 반드시 hashCode도 재정의 해야 한다.
그렇지 않으면 Object.hashCode의 일반 규약을 어기게 되므로, HashMap, HashSet, HashTable같은 해시(hash) 기반 컬렉션과 함께 사용하면 오작동하게 된다.
<Object 클래스 명세에서 복사해 온 일반 규약>
** 주의할 것은, 성능을 개선하려고 객체의 중요부분을 해시 코드 계산과정에서 생략하면 안된다는 것이다.
그렇지 않으면 Object.hashCode의 일반 규약을 어기게 되므로, HashMap, HashSet, HashTable같은 해시(hash) 기반 컬렉션과 함께 사용하면 오작동하게 된다.
<Object 클래스 명세에서 복사해 온 일반 규약>
- 응용프로그램 실행 중에 같은 객체의 hashCode를 여러 번 호출하는 경우, equals가 사용하는 정보들이 변경되지 않았다면, 언제나 동일한 정수(Integer)가 반환되어야 한다. 다만 프로그램이 종료되었다가 다시 실행되어도 같은 값이 나올 필요는 없다.
- equals(Object)메소드가 같다고 판정한 두 객체의 hashCode값은 같아야 한다.
- equals(Object)메소드가 다르다고 판정한 두 객체의 hashCode값은 꼭 다를 필요는 없다. 그러나 서로 다른 hashCode값이 나오면 해시테이블(hashTable)의 성능이 향상될 수 있다.
<이상적인 해시 함수를 만드는 방법>
1. 17과 같은 0 아닌 상수를 result라는 이름의 int 변수에 저장한다.
2. 객체 안에 있는 모든 중요필드 f에 대해서(equals 메소드가 사용하는 필드를 말한다) 아래의 절차를 시행한다.
- A. 해당필드에 대한 int 해시 코드 c를 계산한다.
- 필드가 boolean 이면(f?1:0)을 계산한다.
- 필드가 byte, char, short, int 중 하나면 (int)f 를 계산한다.
- 필드가 long이면 (int)(f^(f>>>32))를 계산한다.
- 필드가 float이면 Float.floatToIntBits(f)를 계산한다.
- 필드가 double이면 Double.doubleToLongBits(f)를 계산하고 그 결과로 얻은 long값을 위의절차 3에 따라 해시코드로 변환한다.
- 필드가 객체 참조이고 equals 메소드가 해당 필드의 equals메소드를 재귀적으로 호출하는 경우에는 해당 필드의 hashCode메소드를 재귀적으로 호출하여 해시코드를 계산한다. 좀더 복잡한 비교가 필요한 경우에는 해당 필드의 "대표형태" 를 계산한 다음, 대표형태에 대해 hashCode를 호출한다. 필드값이 null인경우 0을 반환한다.
- 필드가 배열인 경우에는 배열의 각 원소가 별도 필드인 것처럼 계산한 다,. 즉, 각각의 중요 원소에 대해서 방금 설명한 규칙들을 재귀적으로 적용해 해시 코드를 계산하고, 그 결과를 2.B와 같이 결합한다.
B. 위의 절차 A에서 계산된 해시코드 c를 result에 다음과 같이 결합
result = 31 * result + c;
3. result를 반환
** 주의할 것은, 성능을 개선하려고 객체의 중요부분을 해시 코드 계산과정에서 생략하면 안된다는 것이다.
댓글
댓글 쓰기