ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • equals를 재정의하려거든 hashCode도 재정의하라
    JAVA/Effective java 2021. 1. 14. 11:44
    • equals를 재정의한 클래스 모두에서 hashCOde도 재정의해야 한다.
    • 그렇지 않는다면 hashCode 일반 규약을 어기게 되어 해당 클래스의 인스턴스를 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이다.

     

     

    • equals(Object0가 두 객체를 같다고 판단했다면 두 객체의 hashCode는 똑같은 값을 반환해야 하며 equals(Object) 가 두 객체를 다르다고 판단했더라도, 두 객체의 hashCode가 서로 다른 값을 반환할 필요는 없다.
    • 단, 다른 객체에 대해서는 다른 값을 반환해야 해시 테이블의 성능이 좋아진다.

     

    • 이 코드의 답은 null을 반환한다.
    • PhoneNumber 클래스는 hashCode를 재정의하지 않았기 때문에 논리적 동치인 두 객체가 서로 다른 해시 코드를 반환한다.
    • 그 결과 get 메서드는 엉뚱한 해시 버캣에 가서 객체를 찾으려 하게 된다.

     

     

    그렇다면 hashCode 메서드를 작성해보자.


    좋은 해시 함수라면 서로 다른 인스턴스에 다른 해시 코드를 반환한다.

    이상적인 해시 함수는 주어진 인스턴스들을 32비트 정수 범위에 균일하게 분배해야 한다.

     

     

    • 다음의 곱셈 31 * result 는 필드를 곱하는 순서에 따라 result 값이 달라지게 한다.
    • 그 결과 클래스에 비슷한 필드가 여러 개 라면 해시 효과를 크게 높여준다.
    • 곱할 숫자를 31로 정한 이유는 홀수이면서 소수이기 때문이다.
    • 31을 이용하면 이 곱셈은 시프트 연산과 뺄셈으로 대체해 최적화 가능하게 된다.
    ( i << 5) - i

     

     

     

     

    • 다음과 같이 Objects 클래스는 임의의 개수만큼 객체를 받아 해시코드를 계산해주는 정적 메서드 hash를 제공한다.
    • 이는 단 한줄로 작성할 수 있지만 아쉽게도 속도는 더 느리다.
    • 입력 인수를 담기 위한 배열이 만들어지고, 입력 중 기본 타입이 있다면 박싱과 언박싱도 거치기 때문이다.
    • 그러니 Objects 클래스에 hash 메서드는 성능에 민감하지 않은 상황에서만 사용하자.

     

     

     

    • 해시의 키로 사용되지 않는 경우는 hashCode가 처음 불릴 때 계산하는 지연 초기화 전략이 가능하다.
    • 필드를 지연 초기화하려면 그 클래스를 스레드에 안전하게 만들도록 해야 한다.

     

    정리


     

    • equals를 재정의할 때는 hashCode도 반드시 재정의 해야 한다.
    • 재정의한 hashCode는 Object의 API 문서에 기술된 일반 규약에 따라야 하며 되도록 해시 코드도 다르게 구현해야 한다.
    • AutoValue 프레임워크와 IDE 등은 이런 기능을 일부 제공한다.

     

    Intellij의 Generate Equals() and HashCode()

     

     

     

    참고 자료 


    이펙티브 자바

Designed by Tistory.