프로그래밍/JAVA Spring

[JAVA 자바] equals와 hashcode를 함께 정의해야 하는 이유

hectick 2023. 2. 26. 19:12

두 객체의 주소값이 같다면 두 객체를 물리적으로 같다고 한다. 두 객체가 주소값이 일치하지 않더라도, 두 객체의 필드가 일치하기만 한다면 두 객체가 같다고 판단하는 것은 두 객체는 논리적으로 같다고 한다.

모든 클래스들은 Object 클래스를 상속하는데, Object 클래스에는 equals 메소드가 존재한다. equals를 재정의 하지 않은 상태에서는 두 객체를 비교할 때 물리적 같을 때만 true를 반환한다. 반면 equals를 오버라이딩으로 재정의 해준다면 두 객체가 논리적으로 같을 때도 true를 반환하게 된다.

 

인텔리제이의 Generate 단축키(윈도우 기준 alt + insert)를 사용하면, 인텔리제이는 eqauls와 hashcode를 한번에 재정의하게 한다. 인텔리제이는 괜한 짓을 하지 않는데, 왜 함께 재정의 하도록 한 걸까? 둘을 함께 정의해야하는 중요한 이유가 있는 걸까?

 

Generate 단축키를 눌렀을 때

 

 

🐣equals와 hashcode를 함께 정의해야 하는 이유는?

 

equals만 재정의하고 hashcode를 재정의하지 않는다면, Object 클래스 명세에 나와있는 규약 중에서, 다음 hashCode 규약을 어기게 된다.

equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다.

 

즉, equals만 재정의하고 hashcode를 재정의하지 않는다면 두 객체의 equals를 통한 비교는 같다고 판단되어도, 두 객체는 다른 해시값을 가지게 된다. 이는 HashMap, HashSet, HashTable 같은 해시값을 사용하는 컬렉션을 이용할 때 문제가 된다.

 

예시로 HashSet을 살펴보자. HashSet은 데이터의 중복을 허용하지 않는 자료구조인데, 중복인지 판단할 때 hashCode 메소드로 같은 해시값이 있는지 판단하고, 같은 해시값이 있을 경우 equals 메소드로 같은 객체인지 확인한다. 다음 예시는 equals만 재정의하고, hashCode는 재정의하지 않는 예시이다. 다음 예시에서 객체 car1, car2, new Car("irene")은 모두 논리적으로 같지만, 서로 다른 해시값을 가지고 있다.

 

equals 재정의O, hashCode 재정의X

 

Q: car1아 너 "irene"이라는 이름의 자동차랑 같은 객체지?

A: 응 똑같아

 

Q: car1을 가지고있는 cars야 너 "irene"이라는 이름의 자동차 가지고 있지?

A: 아니 없는데?

 

뭔가 좀 이상하다. 분명 HashSet 안에 들어있는 객체와 동일한 객체를 가지고 있냐고 물어봤는데 없댄다. 이는 HashSet이 우선 두 객체의 hashCode 결과가 같아야 하며, 그 다음으로 equals로도 같다고 판단되어야 중복이라고 판단하기 때문에 벌어진 상황이다. 즉, equals만 재정의하고 hashCode는 재정의하지 않는다면, 해시값을 쓰는 컬렉션에서 같은 객체를 같은 객체라 하지 못하는..... 아이러니한 상황이 발생하게 된다. 

 

이젠 equals와 hashCode를 모두 재정의한 예시를 보자.

 

equals 재정의O, hashCode 재정의O

 

Q: car1아 너 "irene"이라는 이름의 자동차랑 같은 객체지?

A: 응 똑같아

 

Q: car1을 가지고있는 cars야 너 "irene"이라는 이름의 자동차 가지고 있지?

A: 응 가지고 있어

 

이제야 비로소 자연스럽다.

 

 

인텔리제이의 Generate 기능을 통해 equals와 hashCode가 자동완성된 코드는 다음과 같다. 재정의된 equals를 보면, 두 객체의 name이라는 필드의 값이 일치하면 true를 반환하는 것을 알 수 있다. 또, 재정의 된 hashCode를 보면 name을 이용해 해시값을 만드는 것을 확인할 수 있다. 즉, 두 객체의 name 필드의 값이 같다면 반환되는 해시값도 같게 재정의 한 것이다.

 

자동완성된 equals와 hashCode 코드

 

 

난 해시값 같은거 쓸 일 없어~ 하고 정의를 안해버렸다가 프로그램이 확장된 후에 갑자기 사용하게 될 수도 있다. 그 때가 되서야 이상하다는 것을 눈치채고 부랴부랴 오류를 찾기보단 equals를 정의할때 같이 미리미리 hashCode도 정의해 주는게 이로운 것 같다.