본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] equals 메소드를 오버라이드 할 때는 hashCode 메소드도 항상 같이 오버라이드 하자.

by 돼지왕 왕돼지 2016. 10. 5.
반응형

 equals 메소드를 오버라이드 할 때는

 hashCode 메소드도 항상 같이 오버라이드 하자.


17, 31, 31 곱셈, bit operation, collection, Date, Effective JAVA, equals, hascode, hashcode, HashMap, HashSet, Hashtable, integer, lazy init, object.hashcode, override, robustness, string, User, [Effective Java] equals 메소드를 오버라이드 할 때는 hashCode 메소드도 항상 같이 오버라이드 하자., 곱셈, 동일, 두 객체, 명세, 문서화, 보편적 계약, 분산, 불변, 산출 로직, 성능 하락, 성능 향상, 소수, 안도감, 앱, 오버라이드, 완벽, 인스턴스 생성, 일관성 있는 정수, 재실행, 정수, 제외, 좋은 방법이 아니다, 좋은 해쉬 메소드, 컬렉션, 클래스, 해시 값 캐시, 해시 코드 연산 비용


-
equals 메소드를 오버라이드 하는 모든 클래스에서는 반드시 hashCode 메소드도 오버라이드 해야 한다.
그렇게 하지 않으면 Object.hashCode 메소드의 보편적 계약을 위반하게 되어, HashMap 과 HashSet 및 HashTable 을 포함하는 모든 hash 기반 컬렉션들에서 제대로 작동 안 할 수 있다.


-
HashCode 의 메소드 명세 계약 사항은 아래와 같다.
1. equals 메소드에서 비교하는 객체의 값이 변경되지 않는다면, 여러번 호출해도 일관성 있는 정수를 반환해야 한다.
( 단 어플리케이션이 재실행 될 때까지 같을 필요는 없다. )
2. equals 메소드 호출 결과 두 객체가 동일하면, 두 객체 각각에 대해 hashCode 도 같은 정수 값이 나와야 한다.
3. equals 메소드 호출 결과 두 객체가 다르더라도, 두 객체의 hashCode 가 다를 필요는 없다. 그러나 hashCode 가 다르다면 hash 계통 Collection 에서 성능향상을 기대할 수 있다.


-
좋은 해시 메소드는 동일하지 않은 객체들에 대해 서로 다른 해시 코드를 만든다.
동일하지 않은 인스턴스들에 대해 해시 메소드가 모든 가능한 해시 값을 고르게 분산시켜주어야 한다.
완벽하게 구현하는 것은 어렵지만 거의 균일하게 분포하도록 하는 것은 그리 어렵지 않다.
그 방법은 책을 직접 참고하자. ( 타이핑 하기에 길고 복잡하다. )


-
아래와 같은 식으로 계산하면 대체적으로 균등하게 분포된다.
int hashCode = 17; // 17 소수
hashCode = 31 * hashCode + fieldValue1; // 31 곱셈은 bit operation 이 쉬워 성능이 좋다.
hashCode = 31 * hahsCode + fieldValue2;
...

equals 메소드에서 비교하지 않는 필드는 hashCode 산출과정에서 제외시켜야 한다.


-
불변이면서 해시 코드 연산 비용이 큰 클래스는 해시 값을 캐시해놓아 사용하는 것도 좋다.
lazy init 을 할 수도 있고, 인스턴스 생성 시에 산출할 수도 있다.


-
해시 코드를 산출할 떄 성능 향상을 이유로 객체의 중요 부분을 제외시키면 안된다.
해시 코드 생성 자체는 빨라질 수 있으나, 해시 컬렉션들의 성능 하락으로 이어져 더 안 좋은 결과를 초래한다.


-
String, Integer, Date 와 같은 자바 플랫폼 라이브러리의 많은 클래스들이 자신의 hashCode 명세(문서화)를 갖고 있다.
User 에게 안도감을 줄 수는 있지만, 일반적으로 좋은 방법이 아니다.
더 좋은 HashCode 산출 로직이 나왔을 때 명세 때문에 쉽게 바꾸기 어렵기 때문이다.
즉, hashCode 산출 로직은 문서화하지 말고, robustness 에 집중하면 된다.





반응형

댓글