5.2 KiB
자바의 equals() 와 hashCode() 메서드 완벽 정리
1. equals() 와 hashCode()란?
자바에서 객체를 비교할 때, == 연산자는 메모리 주소를 비교한다.
하지만 객체의 내용이 같은지를 비교하려면 equals()를 재정의해야 한다.
또한, HashMap, HashSet 같은 컬렉션에서 객체를 올바르게 저장하고 검색하려면 hashCode()도 재정의해야 한다.
2. equals() 와 hashCode()의 기본 동작
✅ 1. equals() 기본 구현 (Object 클래스)
모든 클래스는 Object 클래스를 상속받으며, Object의 기본 equals()는 메모리 주소를 비교한다.
public boolean equals(Object obj) {
return (this == obj);
}
즉, 기본적으로 equals()는 ==과 동일한 동작을 한다.
✅ 2. hashCode() 기본 구현 (Object 클래스)
Object의 hashCode()는 객체의 메모리 주소를 기반으로 해시 값을 반환한다.
public native int hashCode();
(※ native는 자바가 아닌 C/C++로 구현되었다는 의미)
3. equals()와 hashCode()를 함께 재정의해야 하는 이유
예제: HashSet에서 equals()만 재정의했을 때 발생하는 문제
import java.util.HashSet;
import java.util.Objects;
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
}
public class Main {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("Alice"));
System.out.println(set.contains(new Person("Alice"))); // false (올바르게 동작하지 않음!)
}
}
✔ 이유: HashSet은 hashCode()를 먼저 비교한 후 equals()를 실행하는데, hashCode()가 재정의되지 않아서 contains()가 실패한다.
✅ 해결 방법: hashCode()를 equals()와 함께 재정의한다.
4. equals()와 hashCode() 올바르게 재정의하는 방법
✅ 올바른 equals() 재정의
- 반사성 (
x.equals(x) == true) - 대칭성 (
x.equals(y) == true면y.equals(x) == true) - 추이성 (
x.equals(y) == true&y.equals(z) == true면x.equals(z) == true) - 일관성 (
x.equals(y)의 결과가 변하지 않음) - null 비교 시
false반환 (x.equals(null) == false)
예제 코드:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
✅ 올바른 hashCode() 재정의
✔ 같은 객체는 같은 hashCode()를 가져야 한다.
✔ 다른 객체라도 같은 hashCode()를 가질 수는 있지만 가능하면 충돌을 줄이는 것이 좋다.
예제 코드:
@Override
public int hashCode() {
return Objects.hash(name);
}
Objects.hash()는 null 체크까지 포함된 안전한 방식이다.
5. equals()와 hashCode()를 올바르게 구현한 예제
import java.util.HashSet;
import java.util.Objects;
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public class Main {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("Alice"));
System.out.println(set.contains(new Person("Alice"))); // true (올바르게 동작!)
}
}
✅ equals()와 hashCode()를 함께 재정의했기 때문에 contains()가 정상적으로 동작한다.
6. record를 사용하면 자동 구현됨! (Java 14+)
자바 14부터 도입된 record는 equals()와 hashCode()를 자동 생성해준다.
record Person(String name) {}
public class Main {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("Alice"));
System.out.println(set.contains(new Person("Alice"))); // true
}
}
✔ record는 자동으로 equals()와 hashCode()를 올바르게 구현해준다.
7. 결론
equals()만 재정의하면 HashSet, HashMap 등의 컬렉션에서 문제가 발생할 수 있다.hashCode()도 함께 재정의해야 컬렉션에서 정상 동작한다.record를 사용하면 자동으로equals()와hashCode()가 구현된다.
이제 equals()와 hashCode()를 제대로 이해했으니, 컬렉션을 사용할 때 예상치 못한 버그를 피할 수 있을 것이다!