equals와 hashCode를 함께 재정의하지 않으면 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제가 발생한다.
HashMap.containskey()의 내부 코드를 확인해 보면 두 객체의 hashcode가 같아야 하고 둘의 key 참조가 같거나, equals 반환값이 true일 경우 동일한 객체라고 판단한다.
관련 내용 참조
이 규약을 지키지 못한다면 다음의 문제가 발생하게 된다.
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
// p1과 p2는 논리적 동치
Set<Point> set = new HashSet<>();
set.add(p1);
System.out.println(set.contains(p2)); // false
p1과 p2는 분명 논리적 동치이기 때문에 contains 호출 결과로 true가 나오길 기대했다. 하지만 결과는 false가 나온다.
그 이유는 hashCode 값이 다르기 때문이다. 즉, contains 호출 시 엉뚱한 해시 버킷에 가서 객체를 찾기 때문에 false를 반환하게 된다. 이것은 분명히 의도했던 바가 아니다.
그렇다면 hashCode 호출 시 항상 동일한 값을 반환하도록 한다면 어떨까?
@Override
public int hashCode() {
return 12;
}
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
// p1과 p2는 논리적 동치
Set<Point> set = new HashSet<>();
set.add(p1);
System.out.println(set.contains(p2)); // true
예상했던 대로 true가 나온다. 하지만 이 방법의 경우 모든 객체가 해시테이블의 버킷 하나에 연결 리스트로 이어져 저장되기 때문에 평균 수행 시간이 O(1)에서 O(n)으로 느려지게 된다.
좋은 해시 함수라면 서로 다른 인스턴스에 다른 해시 코드를 반환한다.