equals 를 재정의할 때는 hashCode도 재정의 해야한다. HashMap이나 HashSet 같은 컬렉션의 원소로 인스턴스를 사용할 때 문제를 일으키게 되기 때문이다.
다음은 Object 명세서에서 발최한 규약이다.
- equals 비교에 사용되는 정보가 변경되지 않았다면 어플리케이션이 실행되는 동안 그 객체의 hashCode 메소드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다. 단, 어플리케이션을 다시 실행한다면 이 값이 달라져도 상관 없다.
- equals(Object)가 두 객체를 같다고 판단했다면 두 객체의 hashCode는 똑같은 값을 반환해야 한다.
- equals(Object)가 두 객체를 다르다고 판단했더라도 두 객체의 hashCode가 서로 다른 값을 반환할 필요는 없다. 단 다른 객체에 대해서는 다른 값을 반환해야 해시테이블의 성능이 좋아진다.
hashCode 를 잘못 재정의하게 되면 큰 문제가 되는 조항은 두번째이다. 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다.
다음 hashCode를 정의하지 않으면 발생할 수 있을 문제를 보자.
Map<PhoneNumber, String> map = new HashMap<>();
map.put(new PhoneNumber("010","1234","5678"), "Jenny")
map.get(new PhoneNumber("010","1234","5678")) // Jenny 는 나오지 않는다.
위의 코드의 경우 get의 결과로 "Jenny"가 나올것이라 생각되지만, put에 넣은 PhoneNumber
객체와 get에 넣은 PhoneNumber
객체는 서로 다른 주소값을 가지고 있기 때문에 실제로는 null이 반환된다.
hashCode()의 결과가 서로 다르다면 논리적 동치성을 확인하지 않게 최적화되어 있기 때문에 발생하는 문제다.