일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 깃허브
- IT
- Effective Java
- 운영체제
- 메모리
- 알고리즘
- 프로그래밍
- 디지털
- 개발
- package-private
- 공채
- github
- 이펙티브 자바
- CS
- 스터디
- spring
- 깃
- 자바
- 컴퓨터과학
- 정보처리기사
- 컴퓨터공학
- Public
- 신입
- 신입사원
- 세마포어
- 우리카드
- 스프링
- java
- 뮤텍스
- OS
Archives
- Today
- Total
주니어 개발자 성장기
아이템 11. equals를 재정의하려거든 hashCode도 재정의하라. 본문
개요
equals
와 hashCode
는 항상 같이 구현되어야 한다. IDE, Lombok, AutoValue 등 서도 같이 구현되는 것을 전제로 한다.
일반 규약
equals
비교에 사용하는 정보가 변경되지 않았다면 hashCode는 매번 같은 값을 리턴해야 한다. (변경되거나, 애플리케이션을 다시 실행했다면 달라질 수 있다.)equals
가 두 객체를 같다고 판단했다면,hashCode
의 값도 같아야 한다.- 두 객체에 대한
equals
가 다르더라도,hashCode
의 값은 같을 수 있지만 해시 테이블 성능을 고려해 다른 값을 리턴하는 것이 좋다.
제약사항
equals
에서 사용한 필드들을 모두 사용해야 한다.
이렇게 해야 인스턴스 간의 차이점이hashCode
에 반영되어 다른 인스턴스가 같은 해쉬코드를 가지는 것해쉬 충돌(hash collision) 을 최대한 방지할 수 있다.
구체적으로는, HashMap
에 put을 하면 Key의 hashCode
값으로 hashBucket
에 집어 넣는다. hashBucket
안에는 LinkedList
가 구현되어 있다. 따라서 hashCode
의 값이 동일한 hash collision이 일어나면 탐색의 시간 복잡도가 O(n)이 되는 것이다.
구현 방법
- 첫 번째 핵심필드로 hashCode 값을 구하고
int result
값에 대입한다. - 그 다음 부터 정해진 순서대로 핵심필드들의 hashCode 값을 구해서 기존의 값에 31을 곱한 것에 더한다.
EX)result = 31 * result + Short.hashCode(shortVariable)
- 레퍼런스 타입은 그 타입의
hashCode
메서드를 호출한다. - Primitive 타입은
Type.hashCode(var)
을 호출하면 된다. - Array(배열)은
Arrays.hashCode()
를 호출하면 된다.
- 레퍼런스 타입은 그 타입의
result
를 반환한다.
아래는 예시코드이다.
// 코드 11-2 전형적인 hashCode 메서드 (70쪽)
@Override
public int hashCode() {
int result = Short.hashCode(areaCode); // 1
result = 31 * result + Short.hashCode(prefix); // 2
result = 31 * result + Short.hashCode(lineNum); // 3
return result;
}
31인 이유?
- 홀수
짝수의 경우 0이 채워지면서 정보가 손실될 수 있다.
(컴퓨터에서 변수의 표현이 내부적으로 2진수 이므로) - 어떤 개발자가 사전에서 해싱을 시도했는데 31이 가장 해쉬 충돌이 적게 일어났다고 한다.
실질적인 구현
Object.hash()
를 사용하는 것이 간편하다. (내부적인 구현 방식도 위 코드와 비슷하다)
// 코드 11-3 한 줄짜리 hashCode 메서드 - 성능이 살짝 아쉽다. (71쪽)
@Override public int hashCode() {
return Objects.hash(lineNum, prefix, areaCode);
}
하지만 성능에 민감한 경우 성능이 약간 저하될 수도 있다는 점은 감안하자. (대부분의 경우는 상관 없다.)
- 불변 객체의 경우 미리
hashCode
를 캐싱해 놓을 수 있다. hashCode
를 지연 초기화할 경우 쓰레드 안정성에 신경써야 한다.
정리
equals
를 쓰려면hashCode
도 반드시 재정의하자.equals
에서 사용된 핵심 필드들을hashCode
를 계산할 때 모두 사용하자.- 간편하게 Lombok의
@EqualsAndHashCode
를 사용하자.
→ Lombok을 이용할 경우 단위 테스트를 할 필요 없다는 점도 장점이다.
'Java > 이펙티브 자바' 카테고리의 다른 글
아이템 13. Clone 재정의는 주의해서 진행하라. (0) | 2023.09.02 |
---|---|
아이템 12. toString을 항상 재정의하라. (0) | 2023.09.02 |
아이템 10. equals는 일반 규약을 지켜 재정의하라 (0) | 2023.07.28 |
아이템 9. try-finally 보다 try-with-resources를 사용하라. (0) | 2023.07.23 |
아이템 8. finalizer와 cleaner 사용을 피하라 (0) | 2023.07.22 |