Item11 - equals를 재정의하려거든 hashCode도 재정의하라

2023. 11. 22. 22:46Book/이펙티브 자바

 

규약

 1. equals 비교에 사용하느 정보가 변경되지 않았다면 hashCode는 매번 같은 값으 ㄹ리턴해야 한다.

 2. 두 객체에 대하 equals가 같다면, hashCode의 값도 같아야 한다.

 3. 두 객체에 대한 eqauls가 다르더라도(비효율적인 동작으 일으킬 수 있다.), hashCode의 값은 같을 수 있지만 해시 테이블 성능을 고려해 다른 값을 리턴하는 것이 좋다.

 

 

hashCode 재정의를 잘못햇을 때 크게 문제가 되는 조하은 두 번째다. equals 메서드는 값이 같다면 서로 다른 두 객체를 같다고 할 수 있다. 하지만 hashCode 메서드는 다르다고 판단하여 서로 다른 값을 반환한다.

 

    public static void main(String[] args) {
        Map<PhoneNumber, String> map = new HashMap<>();
        PhoneNumber number1 = new PhoneNumber(123, 456, 7890);

        map.put(number1,  "son");

        String s = map.get(new PhoneNumber(123, 456, 7890));
        System.out.println(s);
    }
    
    ==> null

 

number1과 new PhoneNumber(123, 456, 7890)  eqauls 메서드를 사용하면 true를 리턴하고 논리적 동치이지만 hashMap에서 값을 꺼내올 때 hashCode 메서드를 사용하는데 서로 다른 값을 리턴해 number1의 value를 가져오지 못하는 것이다.

 

그럼 만약 hashCode를 재정의해서 무조건 같은 값을 반환하도록 하면 문제가 생길까?

    public static void main(String[] args) {
        Map<PhoneNumber, String> map = new HashMap<>();
        PhoneNumber number1 = new PhoneNumber(123, 456, 7890);
        PhoneNumber number2 = new PhoneNumber(155, 323, 3434);

        //다른 인스턴스인데 같은 hashcode를 쓴다면?

        System.out.println(number1.equals(number2));
        System.out.println(number1.hashCode());
        System.out.println(number2.hashCode());

        map.put(number1,  "son");
        map.put(number2,  "kim");

       
        String s = map.get(number1);
        System.out.println(s);
    }
    
    ==> false, 12, 12, son

 

Map에서 value값을 잘 가져오는 것 처럼 보인다. 문제는 hashMap에서 value 값을 가져올대 배열에서 인덱스로 콕 집어서 빠르게 가져오는 게 아니라 같은 버킷에서 LinkedList 구조로 연결되어 있는 노드들을 순회하며 비교를 해 가져온다는 것이다. O(1) => O(n) 시간 복잡도가 늘어남

 그러니 서로 다른 두 객체에서 다른 해시 값을 반환하는 것이 좋은 성능을 이끌어 낼 수 있다.