기록창고

equals() override 본문

JAVA

equals() override

방금시작한사람 2019. 12. 28. 00:42

이전 Post에서 hashCode를 오버라이드했음에도 불구하고 set의 사이즈가 2로 나왔다.

이유를 HashMap의 getNode 함수에서 찾을 수 있다.

public V get(Object key) {
    HashMap.Node e;
    return (e = this.getNode(hash(key), key)) == null ? null : e.value;
}

final HashMap.Node<K, V> getNode(int hash, Object key) {
    HashMap.Node[] tab;
    HashMap.Node first;
    int n;
    if ((tab = this.table) != null && (n = tab.length) > 0 && (first = tab[n - 1 & hash]) != null) {
        Object k;
        if (first.hash == hash && ((k = first.key) == key || key != null && key.equals(k))) {
            return first;
        }
        /* 아래내용 */
    }
}

HashMap의 get이 hash한 key값과 key를 들고 getNode()라는 함수를 호출한다.

코드를 대충보자면 first 라는 건 table(아마도 hashtable 예상)에서 가장 첫번째 요소를 가져오는 값이고
key 와 불러온 first.key와 같은지 비교를 하여 참일 경우 first를 리턴해준다.

 

key는 hash값이 아니라 우리가 넣은 키값이다. 입력한 key값과 어떤 테이블에서 가져온 key와 같을 경우 리턴해준다.

Main에서 myClass1와 myClass2가 같은지 비교해보면 다르다고 나오기때문에

서로 다르다고 인식하여 size를 2라고 생각하는 것같다.

 

public class Main {
    public static void main(String[] args) {
        Set<MyClass> set= new HashSet<>();

        MyClass myClass1 = new MyClass("first item");
        MyClass myClass2 = new MyClass("first item");

        set.add(myClass1);
        set.add(myClass2);
        System.out.println(set.size());
        System.out.println(myClass1.equals(myClass2)); // false
    }
}

왜 다르냐면 우리가 정의한 class에는 따로 equals 이라는 함수를 재정의하지 않았기 때문에

Object class 에 있는 equals 함수를 호출한다.


public boolean equals(Object obj) { return this == obj; }

 

따라서 객체가 같지 않으므로 false를 리턴한다.

 

이런 문제를 해결하기 위해서 equals 함수를 override 했다.

public class MyClass {
    public String myClassString;
    public MyClass(String myClassString) {
        this.myClassString = myClassString;
    }

    @Override
    public int hashCode() {
        return this.myClassString.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == this) return true; // 두개의 객체가 같으면 true
        if(obj == null || obj.getClass() != this.getClass()){ 
            return false; // obj 클래스와 this 클래스가 다르거나, null 이면 false
        }

        if (this.myClassString.equals(((MyClass) obj).myClassString)) { 
            // MyClass에는 String 이라는 field 밖에 없기 때문에 myClassString 만 서로 같으면 같은 객체로 인식하기로 했다.
            return true;
        } else {
            return false;
        }
    }
}

 

이제 다시 main을 실행하면 사이즈가 1로 나오는 것을 볼 수 있다!

 

이로써 Map이나 Set을 사용할려면 두개의 함수를 오버라이드할 필요가 있다.

 

그리고 object api에서 equals를 보면

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

이 메소드(equals)를 오버라이드하러면 hashCode 또한 오버라이드 하라. 이라는 문구가 있다.

 

이유는 equals 메소드에서 의해 같다고 여겨지는 객체 A, B가 hashCode()를 거치면 서로 다른 hash 값를 가지는게 말이 안되기 때문이다. 반대로 hash 값이 같다고 같은 객체일 수는 없다.

 

정리하자면 같은 객체의 hash 값은 서로 같아야 한다. 하지만 같은 hash 값을 가지는 객체를 다를 수 있다.

 

 

감사합니다.

'JAVA' 카테고리의 다른 글

String.equals()  (0) 2020.01.19
Collectors GroupingBy  (0) 2020.01.16
JAVA List for 문으로 remove하기  (0) 2020.01.05
hashCode() override  (0) 2019.12.27
static 에 관하여  (0) 2019.12.22
Comments