equals()와 hashCode()

허진혁·2023년 6월 21일
1

기본기를 다지자

목록 보기
1/10

🤔 궁금사항

equals()를 재정의 하면, hashcode()도 재정의 해야 하는 이유는 무엇일까? 각각 메서드의 역할을 중심으로 알아보려 해요.

동일성(Identity)

두 객체가 같다는 의미는 두 가지로 해석할 수 있어요. 두 객체가 동일하다는 것은 두 객체의 메모리 주소값이 같은지를 '=='연산자를 통해 확인하는 것이에요.

동등성(Equality)

두 객체가 동등하다는 것은 두 객체가 담고있는 데이터 값들이 같음을 의미해요. 두 객체의 값이 같은지를 equals() 메서드를 통해 확인할 수 있어요.

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

👉 두 단어를 요약해보면

동일성은 메모리 주소가 같은지 비교하는 것이고,
동등성은 논리적 지위가 같음을 비교하는 것이에요.

실험 1

		String s1 = "test";
        String s2 = "test";

        System.out.println(s1 == s2); // false
        System.out.println(s1.equals(s2)); // true

위 코드의 결과를 보았을 때, 동등함을 알 수 있어요. 만약 s1 == s2도 true였다면 동일한 것이지요.

💡 알고 가야 하는 것

기본형 타입은 Stack에 저장되며, 따로 메모리 주소를 갖고 있지 않아요.
참조형 타입은 Heap에 저장되며, 메모리 주소를 참조하여 값을 가져와요.


❄️ s1, s2는 같은 값을 지니고 있기에 프로그래밍 상에서는 같은 객체로 인식하고 싶은데, 그렇다면 equals 메서드를 재정의하고, hashcode 메서드도 재정의 해야 해요.

먼저 hasCode 메서드를 보아요.

hashCode()

public native int hashCode();

이 메서드는 객체의 해시코드를 정수로 리턴해줘요. native 코드는 JNI(Java Native Interface)라는 native 코드에 이용해 구현되었음을 의미해요.
hashcode는 HashTable, HashSet과 같은 자료구조를 사용할 때, 데이터가 저장되는 위치를 결정하는 역할을 해요.

equals와 hashCode의 관계

자바 api 문서를 볼게요.

hashCode() 메소드를 구현할 때 다음과 같은 약속을 지키라고 api docs에 명시되어 있어요.

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
  • 자바 애플리케이션이 수행되는 동안 어떤 객체에 대해 이 메소드가 호출될 때에는 항상 동일한 int 값을 리턴해 주어야 한다.(단, 자바를 실행할 때마다 같은 값일 필요는 없다.)

  • 어떤 두 객체에 대하여 equlas() 메소드를 사용하여 비교한 결과 true라면, 두 객체의 hashCode() 메소드를 호출하면 동일한 int값을 리턴해야 한다.

  • 두 객체를 equals() 메소드를 사용하여 비교한 결과 false를 리턴했다고 해서, hashCode() 메소드를 호출한 int 값이 무조건 다를 필요는 없다. 하지만, 이 경우에 서로 다른 int 값을 제공하면 hashtable의 성능을 향상시키는데 도움이 된다.

정리해보면, 동일한 객체는 동일한 메모리 주소를 갖는다는 것을 의미해요. 생각해보면, 동일한 객체는 동일한 해시 코드를 갖는다는 것이죠. 그래서 equals 메서드를 재정의하면 hashcode 메서드도 재정의 해야 해요.

재정의한 equals()와 hashCode()

public class Identity {

    private final String name;
    private int num;

    // constructor, getter, setter...

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Identity identity = (Identity) obj;
        return this.name.equals(identity.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

실험 1 - equals()만 재정의

Identity firstIdentity = new Identity("number");
Identity secondIdentity = new Identity("number");

HashSet<Identity> identities = new HashSet<>();

identities.add(firstIdentity);
identities.add(secondIdentity);

System.out.println(identities.size()); // 2

이상한 점이 발견되었어요. 두 객체가 같다고 equals 메서드를 재정의 했으면, Set 자료구조상 동일한 객체가 있으면 안되요. 그런데 두 객체 모두 HashSet에 포함되어 있어요. 이러한 문제를 해결하기 위해 hashCode 메서드도 재정의하는 것이에요.

실험 2 - hashCode()도 재정의

Identity firstIdentity = new Identity("number");
        Identity secondIdentity = new Identity("number");


```java
Identity firstIdentity = new Identity("number");
Identity secondIdentity = new Identity("number");

        System.out.println(firstIdentity.equals(secondIdentity)); // true

HashSet<Identity> identities = new HashSet<>();

identities.add(firstIdentity);
identities.add(secondIdentity);

System.out.println(identities.size()); // 1

이제 의도한 대로 HashSet의 길이는 1이에요. hashcode를 통해 식별값을 기준으로 해싱함수를 실행시켜 equals 메서드를 통해 동일함이 증명되었으면 같은 메모리 주소값을 갖게 되었어요.

💪 정리

  1. 동일성(Identity) 비교는 '==' 연산자를 통해 메모리 주소값이 같은지 비교하는 것입니다.

  2. 동등성(Equality) 비교는 equals() & hashCode() 를 통해 논리적 지위가 같은지 비교하는 것입니다.

  3. 논리적 지위가 같다의 기준은 개발자가 요구사항에 맞게 오버라이드하여 재정의해야 합니다.

  4. equals 메소드에서는 "두 객체가 같다" 의 기준이 될 필드 변수(들)을 비교하도록 재정의합니다.

  5. hashCode 메소드에서는 "두 객체가 같다" 의 기준이 될 필드(들)의 값으로 hashCode를 만들도록 재정의합니다.

profile
Don't ever say it's over if I'm breathing

0개의 댓글