(Effective Java) equals를 재정의할 때는 반드시 hashCode도 재정의 하라

* equals를 재정의할 때는 반드시 hashCode도 재정의 해야 한다.
그렇지 않으면 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를 계산한다.
  1. 필드가 boolean 이면(f?1:0)을 계산한다.
  2. 필드가 byte, char, short, int 중 하나면 (int)f 를 계산한다.
  3. 필드가 long이면 (int)(f^(f>>>32))를 계산한다.
  4. 필드가 float이면 Float.floatToIntBits(f)를 계산한다.
  5. 필드가 double이면 Double.doubleToLongBits(f)를 계산하고 그 결과로 얻은 long값을 위의절차 3에 따라 해시코드로 변환한다.
  6. 필드가 객체 참조이고 equals 메소드가 해당 필드의 equals메소드를 재귀적으로 호출하는 경우에는 해당 필드의 hashCode메소드를 재귀적으로 호출하여 해시코드를 계산한다. 좀더 복잡한 비교가 필요한 경우에는 해당 필드의 "대표형태" 를 계산한 다음, 대표형태에 대해 hashCode를 호출한다. 필드값이 null인경우 0을 반환한다.
  7. 필드가 배열인 경우에는 배열의 각 원소가 별도 필드인 것처럼 계산한 다,. 즉, 각각의 중요 원소에 대해서 방금 설명한 규칙들을 재귀적으로 적용해 해시 코드를 계산하고, 그 결과를 2.B와 같이 결합한다. 
B. 위의 절차 A에서 계산된 해시코드 c를 result에 다음과 같이 결합
result = 31 * result + c;

3. result를 반환




** 주의할 것은, 성능을 개선하려고 객체의 중요부분을 해시 코드 계산과정에서 생략하면 안된다는 것이다.



댓글

이 블로그의 인기 게시물

(18장) WebSocekt과 STOMP를 사용하여 메시징하기

(네트워크)폴링방식 vs 롱 폴링방식

(ElasticSearch) 결과에서 순서 정렬