equals() / hashCode 재정의
동일한 객체는 동일한 메모리 주소를 갖는다는 것을 의미하므로, 동일한 객체는 동일한 해시코드를 가져야 한다
- Java 프로그램을 실행하는 동안 equals에 사용된 정보가 수정되지 않았다면, hashCode는 항상 동일한 정수값을 반환해야 한다. (Java의 프로그램을 실행할 때 마다 달라지는 것은 상관이 없다.)
- 두 객체가 equals()에 의해 동일하다면, 두 객체의 hashCode() 값도 일치해야 한다.
- 두 객체가 equals()에 의해 동일하지 않다면, 두 객체의 hashCode() 값은 일치하지 않아도 된다.
1. equals만 재정의할 경우
public class Car {
private final String name;
public Car(String name) {
this.name = name;
}
// intellij Generate 기능 사용
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return Objects.equals(name, car.name);
}
}
- this == o → 같은 객체면 true
- o == null 또는 getClass() != o.getClass() → null이거나 다른 클래스면 false
- 마지막 줄 → name 필드값이 같으면 true
Car 클래스에서 equals만 재정의 했음
public static void main(String[] args){
Car car1 = new Car("foo");
Car car2 = new Car("foo");
// true 출력
System.out.println(car1.equals(car2));
}
➡️ equals를 재정의했기 때문에 Car 객체의 name이 같은 car1, car2 객체는 논리적으로 같은 객채로 판단
2. Collection에 중복되지 않는 Car 객체만 넣으라는 요구사항이 추가되었다고 가정
public static void main(String[] args) {
Set<Car> cars = new HashSet<>();
cars.add(new Car("foo"));
cars.add(new Car("foo"));
System.out.println(cars.size());
}
➡️ 추가된 두 Car 객체의 이름이 같아서 논리적으로 같은 객체라 판단하고 HashSet의 size가 1이 나올 거라 예상했지만, 예상과 다르게 2가 출력
hashCode를 equals와 함께 재정의하지 않으면 코드가 예상과 다르게 작동하는 위와 같은 문제를 일으킨다. 정확히 말하면
hash 값을 사용하는 Collection(HashSet, HashMap, HashTable)을 사용할 때 문제가 발생한다.
왜 그럴까?
hash 값을 사용하는 Collection(HashMap, HashSet, HashTable)은 객체가 논리적으로 같은지 비교할 때 아래 그림과 같은 과정을 거친다
hashCode 메서드의 리턴 값이 우선 일치하고 equals 메서드의 리턴 값이 true여야 논리적으로 같은 객체라고 판단
Car 클래스에는 hashCode 메서드가 재정의 되어있지 않아서 Object 클래스의 hashCode 메서드가 사용되었다.
Object 클래스의 hashCode 메서드는 객체의 고유한 주소 값을 int 값으로 변환하기 때문에 객체마다 다른 값을 리턴한다. 두 개의 Car 객체는 equals로 비교도 하기 전에 서로 다른 hashCode 메서드의 리턴 값으로 인해 다른 객체로 판단된 것이다.
3. hashCode 재정의
앞 문제를 해결하기 위해 hashCode 메서드를 재정의
public class Car {
private final String name;
public Car(String name) {
this.name = name;
}
// intellij Generate 기능 사용
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
- Objects.hash(name)는 내부적으로 name.hashCode()를 호출해서 계산
- 즉, Car 객체의 name이 같다면 같은 해시값을 생성
회고
벌써 10주차가 되었다니...!! 시간이 빨리 흐른게 믿기지가 않는다... 스프링 공부하면서 궁금했던 equals와 hashCode에 대해 정리해 보았다... 이제 곧 토이프로젝트를 시작한다고 하는데 아직 스프링이랑 친해지지 못해서 열심이 남은 시간 동안 친해져 봐야겠다. 그리고 공부도 하고 운동도 하나보니 둘 다 제대로 하고 있다는 느낌을 못받아서(운동을 안하면 체력이 훅 떨어지는게 느껴짐...ㅠ 안할 수가 없다...) 시간대를 좀 다르게 해봐야할 것 같은 느낌이 든다.
참고 블로그
https://tecoble.techcourse.co.kr/post/2020-07-29-equals-and-hashCode/
https://mangkyu.tistory.com/101
'회고' 카테고리의 다른 글
[커널아카데미] 백엔드 12기 9주차 - 회고 (0) | 2025.05.23 |
---|---|
[커널아카데미] 백엔드 12기 8주차 - 회고 (0) | 2025.05.16 |
[커널아카데미] 백엔드 12기 7주차 - 회고 (0) | 2025.05.16 |
[커널아카데미] 백엔드 12기 6주차 - 회고 (0) | 2025.05.07 |
[커널아카데미] 백엔드 12기 5주차 - 회고 (1) | 2025.04.25 |