equals()와 hashcode() 메소드의 역할과 어떤 상황에서 사용되는지 설명해보자.
Java에서는 equals()
와 ==
는 차이가 있다.
equals()
메서드는 Object 클래스에 정의된 메서드로, 두 객체가 동등한지를 비교한다. 기본적으로 equals() 메서드는 두 객체의 참조(주소)가 아닌 내부의 값(데이터)
을 비교한다. 따라서 두 객체가 동일한 값을 가지고 있으면 equals() 메서드는 true를 반환한다. equals() 메서드는 필요에 따라 클래스마다 재정의(override)하여 동등성을 비교하는 방식을 변경할 수 있다.
반면에 ==
연산자는 두 개체의 참조(주소)가 동일
한지를 비교한다. 즉, == 연산자는 객체의 실제 값이 아닌 객체의 메모리 주소를 비교한다. 따라서 == 연산자는 두 객체가 정확히 동일한 객체인지를 확인한다.
코드로 살펴본다면
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true
System.out.println(str1.equals(str2)); // true
String s1 = new String("hi");
String s2 = new String("hi");
System.out.println(s1 == s2); // false -> 객체의 주소값을 비교하기 떄문
System.out.println(s1.equals(s2)); // true
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s1.hashCode() == s2.hashCode()); // false
System.out.println(s1 == s2); // false -> 객체의 주소값을 비교하기 떄문
주소 값을 참조하기 때문에 결과 값이 false
인것을 확인할 수 있다.
System.out.println(s1.equals(s2)); // true
System.out.println(s1.hashCode() == s2.hashCode()); // false
String 객체에서의 equals()
와 hashCode()
는 아래와 같이
// String.java
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
public int hashCode() {
int h = hash;
if (h == 0 && !hashIsZero) {
h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
if (h == 0) {
hashIsZero = true;
} else {
hash = h;
}
}
return h;
}
재정의하여 사용하기 때문에 값이 같다면 true
가 나올 수 있다.
package day1;
import java.util.Objects;
public class Man {
String name;
public Man(String name) {
this.name = name;
}
}
Man man1 = new Man("hmmini");
Man man2 = new Man("hmmini");
System.out.println(man1.equals(man2)); //false -> equals 메소드를 재정의해야함
System.out.println(man1.hashCode()); //713338599
System.out.println(man2.hashCode()); //168423058
System.out.println(man1.hashCode() == man2.hashCode()); //false
String 객체처럼 재정의가 안되어 있는 클래스를 살펴보면
// Object.java
public boolean equals(Object obj) {
return (this == obj);
}
@IntrinsicCandidate
public native int hashCode();
System.out.println(man1.equals(man2)); //false
System.out.println(man1.hashCode() == man2.hashCode()); //false
위와 같이 같은 값이 들어가 있음에도 Man
의 equals()는 ==
를 사용하여 주소 값을 비교하기 때문에 false
라는 결과 값을 얻을 수 있다.
따라서 같은 값이 들어가 있는 객체는 객체의 동등성
을 위배해서는 안되고 또한 동일한 객체이므로 동일성
을 위해 동일한 해시코드를 반환해야 한다.
따라서 eqauls()
와 hashCode()
메서드를 오버라이딩하여 재정의해야 한다.
package day1;
import java.util.Objects;
public class Woman {
String name;
public Woman(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Woman person = (Woman) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
위와 같이 equals()
와 hashCode()
를 오버라이딩하여 동등성, 동일성에 위배되지 않도록 재정의해주면
Woman woman1 = new Woman("mini");
Woman woman2 = new Woman("mini");
System.out.println(woman1.equals(woman2)); //true -> equals를 재정의하였음
System.out.println(woman1.hashCode());
System.out.println(woman2.hashCode());
System.out.println(woman1.hashCode() == woman2.hashCode()); //true -> overiding으로 재정의하였음
위와 같이 메서드를 재정의 해주었기 때문에
System.out.println(woman1.equals(woman2)); //true
System.out.println(woman1.hashCode() == woman2.hashCode()); //true
두 객체의 equals()
와 hashCode()
에 대한 결과가 true
임을 확인 할 수 있다.