Item 11. equals를 재정의하려거든 hashCode도 재정의하라

심규환·2022년 1월 20일
0

Effective Java

목록 보기
11/29
post-thumbnail

만약 당신이 equals를 재정의 했다면 hashCode도 재정의해라. 그렇지 않으면 hashCode 일반 규약을 어기게 될 것이다. 이는 HashMap, HashSet 같은 컬렉션을 사용할 때 문제가 생긴다.
다음은 hashCode의 일반 규약이다.

  • equals 비교에 사용되는 값들이 변경되지 않았다면, hashCode 메서드는 항상 같은 값을 반환해야 한다.
  • equals가 두 객체가 같다고 판단했다면, hashCode도 똑같은 값을 반환해야 한다.
  • equals가 두 객체가 다르다고 판단했다면 hashCode가 꼭 같은 값일 필요는 없지만 다른 값일 때, 해시 테이블 성능이 떨어지게 된다.

일반 규약 세 가지 중에서 재정의를 잘못했을 경우, 가장 문제가 되는건 두 번째 조항이다.
- 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다.

만약 논리적으로 같은 객체인데 다른 해시코드를 반환한다면 HashMap에 들어간 객체를 같은 키로 요청했을 경우, null이 나오게 된다. 내가 원하는 값을 받을 수가 없게 된다.

그러면 적절한 hashCode 작성법은 무엇일까?
다음은 hashCode 작성 요령이다.

1. int 변수 result를 선언한 후, 첫 번째 핵심 필드를 해시코드로 계산하여 넣어준다.

2. 각 필드 별, 해시코드 계산 방법이다.

  • 기본 타입 필드 : Type.hashCode(field) 를 수행하여, 기본 타입이 제공하는 해시코드 메소드를 사용하라
  • 참조 타입 필드 : 이 필드의 클래스가 equals 메서드가 있다면, 이 필드의 equals를 재귀적으로 호출하면서 hashCode도 재귀적으로 호출한다. 만약 계산이 복잡해진다면, 이 필드의 표준형을 만들어 표준형의 hashCode를 호출한다. 필드 값이 null이면 0을 사용한다.
  • 배열 필드 : 배열 안에 핵심 원소가 없다면 0, 있다면 Arrays.hashCode를 사용한다.

3. 계산한 핵심 필드의 해시 코드들을 result = 31 * result + c(해시 코드 값)을 하여 result에 계속 갱신 후 반환한다.

여기까지 hashCode를 구현했다면 동치인 인스턴스가 똑같은 해시코드(일반 규약 2번째)를 반환하는지 테스트하자.

전형적인 hashCode메서드

@Override public int hashCode(){
	int result = Short.hashCode(areaCode);
    result = 31 * result + Short.hashCode(prefix);
    result = 31 * result + Short.hashCode(lineNum);
    return result;
}

단순히 Object 클래스의 hashCode 메서드를 사용해서 hashCode를 생성할 순 있지만 내부적으로 배열을 만들고 기본 타입이 있다면 박싱, 언박싱 등 여러가지를 하기 때문에 성능이 떨어진다.
가능하다면 AutoValue 프레임워크를 사용하여 만드는게 좋다.

profile
장생농씬가?

0개의 댓글