LALA's blog
equals() 와 hashCode() 본문
equals와 hashCode는 모든 Java 객체의 부모 객체인 Object 클래스에 정의되어 있다.
그렇기 때문에 Java의 모든 객체는 Object 클래스에 정의된 equals와 hashCode 함수를 상속받고 있다.
equals()
- boolean equals(Object obj)로 정의된 equals 메소드는 2개의 객체가 동일한지 검사하기 위해 사용된다.
- eqauls가 구현된 방법은 2개의 객체가 참조하는 것이 동일한지를 확인하는 것이다.
- 즉, 2개의 객체가 가리키는 곳이 동일한 메모리 주소일 경우에만 동일한 객체가 된다.
- 모든 객체는 equals 메서드를 재정의하여 사용할 수 있다.
equals 로 동치관계를 구현하는데, 이를 정의하기 위한 규칙이 있다.
- 반사성 : 객체는 자기 자신과 같아야 한다.
- 대칭성 : 두 객체는 서로에 대한 동치 여부에 똑같이 답해야 한다.
- 추이성 : a 객체와 b 객체가 같고, b 객체와 c 객체가 같다면 a, c 객체는 같아야 한다.
- 일관성 : 두 객체가 같다면 앞으로도 영원히 같아야 한다.
- null 이 아님 : null 이 아닌 모든 객체는 null 과 같지 않아야 한다.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PieceDto pieceDto = (PieceDto) o;
return Objects.equals(position, pieceDto.position) && Objects.equals(color, pieceDto.color) && Objects.equals(type, pieceDto.type);
}
hashCode()
equals를 재정의한 클래스 모두에서 hashCode 도 재정의해야 한다. 동일한 객체라면 동일한 해시코드를 가져야하기 때문이다. 그렇지 않으면 hashCode 일반 규약을 어기게 되어 HashMap 이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이다.
- equals 비교에 사용되는 정보가 변경되지 않았다면, 애플리케이션이 실행되는 그 객체의 hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다.
- equals(Object) 가 두 객체를 같다고 판단했다면, 두 객체의 hachCode는 똑같은 값을 반환해야 한다.
- equals(Object) 가 두 객체를 다르다고 판단했더라도, 두 객체의 hashCode 가 서로 다른 값을 반환할 필요는 없다. 단, 다른 객체에 대해서는 해시코드가 다르게 반환해야 해시테이블의 성능이 좋아진다.
- why?
- 다른 객체가 해시테이블의 같은 버킷에 담기게 되면 그 안에서 또 탐색을 해야하기 때문이다.
- why?
@Override
public int hashCode() {
return Objects.hash(position, color, type);
}
Objects 클래스는 임의의 개수만큼 객체를 받아 해시코드를 계산해주는 정적 메서드인 hash를 제공한다.
하지만 직접 구현하는 것보단 속도가 느려서 성능이 살짝 아쉽다. 입력 인수르 ㄹ담기 위한 배열이 만들어지고, 입력 중 기본 타입이 있다면 박싱과 언박싱도 거쳐야하기 때문이다.
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
클래스가 불변이고 해시코드를 계산하는 비용이 크다면, 매번 새로 계산하기 보다는 캐싱하는 방식을 고려해보자.
이 타입의 객체가 주로 해시의 키로 사용될 것 같다면 인스턴스가 만들어질 때 해시코드를 계산해둬야 한다. 해시의 키로 사용되지 않는 경우라면 hashCode 가 처음 불릴 때 계산하는 지연 초기화 전략은 어떨까?
private int hashCode; // 자동으로 0으로 초기화된다.
@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
hashCode = result;
}
return result;
}
참고
- 이펙티브 자바 [아이템 10] equals 는 일반 규약을 지켜 재정의하라.
- 이펙티브 자바 [아이템 11] equals 를 재정의하려거든 hashCode 도 재정의하라.
'개발 > 자바' 카테고리의 다른 글
Exception (0) | 2022.04.16 |
---|---|
Collection 복사 방법 - 방어적 복사, unmodifiable, copyOf (0) | 2022.04.16 |
HashMap (0) | 2022.04.16 |