잡담

Equal Objects Must Have Equal Hashcodes

tomato13 2010. 8. 28. 17:01

먼저 출처를 밝혀야할 듯 하다. 

http://luckfellow.blog.me/90027810862


사례1)


public class Person {

    private final String name;

    public Person(String name){

        this.name = name;

    }

    @Override

    public boolean equals(Object obj) {

        if (obj == null) {

            return false;

        }

        if (getClass() != obj.getClass()) {

            return false;

        }

        return name.equals(((Person)obj).name);

    }

}


public class Test {

    public static void main(String[] arg){

        Map<Person, Integer> map = new HashMap<Person, Integer>();

        map.put(new Person("test"), 20);

        map.put(new Person("test"), 20);

        System.out.println(map.size());

    }

}


위의 코드를 수행하면 map의 size는 2가 된다. map 객체는 같은 obj를 중복되게 넣지 않는 컨셉을 가지고 있기에 size가 1이라고 생각을 할 수도 있을 것이다. 

이는 Person의 hashCode() 메서드를 정의하지 않아서이다. map은 obj를 넣기 전에 hash값을 먼저 확인을 하고(hash값을 key로 사용) 그 다음에 equal에 의해서 같은지를 확인하게 된다.

(즉, 위의 코드는 hashCode()가 정의되지 않아서 vm에서 던져주는 임의의 arbitrary값을 hash값으로 할당받기에 항시 다르게 인식되는 것이다.)


때문에 java에서는 equal()을 구현시에 항시 hashcode()도 함께 구현할 것을 권고하고 있다.


사례2)


그렇다면 다음의 코드는 어떨까?

public class Person {

    private String name;

    public Person(String name) {

        super();

        this.name = name;

    }

    public void setName(String name) {

        this.name = name;

    }

    @Override

    public int hashCode() {

        final int PRIME = 31;

        int result = 1;

        result = PRIME * result + ((name == null) ? 0 : name.hashCode());

        return result;

    }

    @Override

    public boolean equals(Object obj) {

        if (this == obj)

            return true;

        if (obj == null)

            return false;

        if (getClass() != obj.getClass())

            return false;

        final Person other = (Person) obj;

        if (name == null) {

            if (other.name != null)

                return false;

        } else if (!name.equals(other.name))

            return false;

        return true;

    }

}


public class Test {

    public static void main(String[] arg){

        Set<Person> set = new HashSet<Person>();   

        Person p1 = new Person("바꾸기전");

        set.add(p1);

        p1.setName("바꾼후");

        Person p2 = new Person("바꾼후");

        set.add(p2);

        

        System.out.println(set.size()); 

    }

}


이 경우에는 Person의 hashCode(), equal() 메서드를 모두 구현하였다. set은 두 개의 Person 객체의 hash 값이 다르기에 모두 등록을 하여 size는 2로 나타날 것이다.

그러나 set에 p1을 add한 후에 p1의 attribute를 "바꾼후"로 변경을 하였다. 즉, 사실상 같은 object가 된 것이다. (여기서 같다는 의미-equal-는 object의 attribute의 값들이 모두 같다는 의미이다.)

하지만 set은 계속해서 두 객체의 hash 값(key value)은 다른 것으로 인식을 하게 된다.

(즉, 위와 같이 사용을 하면 안 되는 것이다.)


따라서 다음과 같이 정리할 수 있을 것이다.

Equal Objects Must Have Equal Hashcodes