아이템 11 equals를 재정의하려거든 hashCode도 재정의하라
equals 메서드를 재정의했다면 반드시 hashCode도 재정의해야 한다. 그렇지 않으면, 두 객체가 논리적으로 같더라도 다른 해시값을 반환하여 해시 기반 컬렉션(HashMap, HashSet)에서 일관성이 깨지거나 제대로 작동하지 않을 수 있다.
1. hashCode와 equals의 관계
- 일반 규약에 따르면, 두 객체가 equals에 의해 같다고 판별되면, 반드시 같은 hashCode를 반환해야 한다.
- 그렇지 않으면, 같은 논리적 객체가 서로 다른 해시 버킷에 들어가고, 해시 기반 자료구조가 올바르게 동작하지 않게 된다.
2. hashCode 규약
- 객체의 동일성을 나타내는 필드를 기준으로 일관된 해시코드를 반환해야 한다.
- 다음의 규칙을 따라야 한다:
- 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다. 즉, 같은 실행 중에 두 번 호출된 경우 항상 같은 해시코드를 반환해야 한다.
- 두 객체가 equals로 같으면 반드시 같은 해시코드를 반환해야 한다.
- 두 객체가 equals로 다르면 같은 해시코드를 반환할 필요는 없지만, 해시 충돌을 피하기 위해 가능하면 다른 값을 반환하는 것이 좋다.
좋은 hashCode 작성 방법
1. 이미 정의된 메서드를 활용하라
자바 표준 라이브러리에서 제공하는 Integer.hashCode, Objects.hash, Arrays.hashCode 등은 효율적이고 검증된 방식이므로 이를 활용하는 것이 좋다. 직접 계산 로직을 작성할 필요가 없다.
2. equals에 사용하지 않는 필드는 hashCode에서 제외
equals에 포함되지 않은 필드를 hashCode에 포함시키면 일관성을 깨트리게 되므로, 반드시 제외해야 한다. 예를 들어, 논리적 동치성에 포함되지 않는 가변 필드나 계산된 값은 hashCode에 넣지 않는다.
3. 해시 충돌을 최소화
해시 충돌을 줄이기 위해 충분히 다양한 해시코드를 반환해야 한다. 해시 충돌이 많으면 해시 기반 자료구조의 성능이 떨어지기 때문에, 해시코드를 구성하는 필드들의 값이 다양하게 반영되도록 계산한다.
4. Guava의 Hashing 사용
고급 해싱 알고리즘이 필요하다면 Guava 라이브러리의 Hashing을 참고할 수 있다. Guava는 성능과 충돌 방지 면에서 우수한 해싱 알고리즘을 제공한다.
5. hashCode 값 생성 규칙 공개 금지
hashCode의 반환 값은 언제든지 변경될 수 있다. 사용자가 이 값에 의존해 코드를 작성하면, 이후 구현이 바뀌었을 때 예기치 않은 오류가 발생할 수 있다. 따라서 hashCode의 내부 생성 규칙은 외부에 공개하지 않아야 한다.
핵심 정리
equals를 재정의할 때는 hashCode도 반드시 재정의해야 한다. 그렇지 않으면 프로그램이 제대로 동작하지 않을 것이다. 재정의한 hashCode느 Object의 API 문서에 기술된 일반 규약을 따라야 하며, 서로 다른 인스턴스라면 되도록 해시코드도 서로 다르게 구현해야 한다. 이렇게 구현하기가 어렵지는 않지만 조금 따분한 일이긴 하다. 하지만 아이템 10에서 이야기한 AutoValue 프레임워크를 사용하고 멋진 equals와 hashCode를 자동으로 만들어준다. IDE들도 이런 기능을 일부 제공한다.
'Java' 카테고리의 다른 글
이펙티브 자바(Effective Java) 2장 - 아이템 13 clone 재정의는 주의해서 진행하라 (0) | 2024.12.03 |
---|---|
이펙티브 자바(Effective Java) 2장 - 아이템 12 toString을 항상 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 10 equals는 일반 규약을 지켜 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 9 try-finally보다는 try-with-resources를 사용하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 8 finalizer와 cleaner 사용을 피하라 (0) | 2024.12.03 |