-
[JAVA] Object 클래스의 equals()와 hashCode() 재정의프로그래밍 언어/JAVA 2022. 10. 15. 01:15
자바 면접 질문에 equals와 hashcode의 차이점에 대한 문항이 있었다. 그때 hashCode에 대해 글로만 읽어봤었는데 자바 책 공부를 하다 equals와 hashCode를 목적에 맞게 같이 오버라이딩하는 것을 보고 다시 정확히 정리하기 위해 포스팅을 작성한다.
📑 Object 클래스의 메서드 - equals()
매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean 값으로 알려주는 역할을 한다.
public boolean equals(Object obj) {
return (this==obj);
}위의 코드는 Object 클래스의 정의되어 있는 equals 메서드의 실제 내용이다. 위 코드에서 알 수 있듯이 두 객체의 같고 다름을 참조변수의 값으로 판단한다. 그렇기 때문에 서로 다른 두 객체를 equals 메서드로 비교하면 항상 false를 얻게 된다.
참고로) ==와 equals()의 비교에선 ( ==는 call by reference로 주소의 값을 비교 즉 실제 값이 아닌 자료의 위치값을 비교하는 것, equals()는 call by value로 값을 비교 즉) 문자열 구성이 같은지 확인 ) 이렇게 알고있었기에 착각했다. 여기선 Object 클래스의 메서드 equals()이니 개념 잡기!
★ 아래에서 사용한 str.equals(str3)는 string 클래스의 equals이다.
package part4.collection; import java.sql.SQLOutput; public class Equals { public static void main(String[] args) { String str = "JAVA"; String str2 = "JAVA"; String str3 = new String("JAVA"); System.out.println(str==str2); //true System.out.println(str==str3); //false System.out.println(str.equals(str3)); //true } }
package part_9; import com.sun.jdi.Value; public class Ex9_1_equals { public static void main(String[] args) { //서로 다른 객체 Value1 v1 = new Value1(10); Value1 v2 = new Value1(10); if(v1.equals(v2)) System.out.println("v1과 v2는 같다."); else System.out.println("v1과 v2는 다르다."); String s = "ss"; String s1 = "ss"; System.out.println(s==s1); System.out.println(s.equals(s1)); String s3 = new String("DD"); String s4 = new String("DD"); System.out.println(s3==s4); System.out.println(s3.equals(s4)); } } class Value1{ int value; Value1(int value){ this.value = value; } }
value라는 멤버변수를 갖는 Value 클래스를 정의하고, 두 개의 Value 클래스 인스턴스를 생성한 다음 equals 메서드를 이용해 두 인스턴스를 비교했다. equals() 메서드는 주소값을 비교하기 때문에 값이 10으로 같을지라도 참조하고 있는 주소값이 다르기에 false, v1과 v2는 다르다는 결과가 출력된다.
🔨 그럼 어떻게 해야 Object 클래스의 equals() 메서드를 통해 같은 값으로 나타낼 수 있을까??
📑 equals()의 오버라이딩
바로 equals()의 오버라이딩이 필요하다. Object 클래스로부터 상속받은 equals 메서드는 결국 두 개의 참조변수가 같은 객체를 참조하고 있는지, 두 참조변수에 저장된 값(주소값)이 같은지를 판단하는 기능밖에 할 수 없다는 걸 알 수 있다. 그럼 equals 메서드로 Value 인스턴스가 가지고 있는 value 값을 비교하기 위해선 메서드 오버라이딩을 통해 주소가 아닌 객체에 저장된 내용을 비교하게하면 된다.
package part_9; public class Ex9_2_equals2 { public static void main(String[] args) { Person p1 = new Person(8011L); Person p2 = new Person(8011L); if(p1.equals(p2)) System.out.println("p1과 p2 같음"); else System.out.println("p1과 p2 다름"); } } class Person{ long id; public boolean equals(Object obj){ if(obj instanceof Person) //obj가 Object 타입이므로 id값 참조 위해 Person 타입으로 형변환. //주소가 아닌 값을 비교해서 같으면 true return id==((Person)obj).id; else //타입이 Person이 아님 비교할 필요도 없다. return false; } public Person(long id) { this.id = id; } }
이렇게 오버라이딩을 했을 때 결과는 p1과 p2가 같다라는 값이 출력된다.
그렇다면 다음 예시를 살펴보기 위해 위의 Ex9_2_equals 클래스에서 메인 메서드에 아래와 같은 코드를 추가해보자.
List<Person> persons = new ArrayList<>(); persons.add(new Person(8011L)); persons.add(new Person(8011L)); System.out.println(persons.size());
list는 중복을 허용하니까 값이 2개가 나올 것이다. 그렇다면 Collection에 중복을 허용하지 않는 Set에 값을 저장해보자. 그렇다면 우리가 equals() 메서드를 오버라이딩 했으니 1개만 나오겠다고 예상할 수 있다.
그런데!!!! 값이 2가 출력되었다. 여기서 hashCode를 생각해봐야한다.
📑 hashCode()의 오버라이딩
Object 클래스의 hashCode()는 해싱 기법에 사용되는 '해시함수'를 구현한 것이다. 해싱은 데이터관리 기법 중 하나로 다량의 데이터를 저장하고 검색하는데 유용하다. 해시함수는 찾고자 하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hash code)를 반환한다.
일반적으로 해시코드가 같은 두 객체가 존재하는 것이 가능하지만, Object 클래스에 정의된 hashCode 메서드는 객체의 주소값을 이용해 해시코드를 반환하기 때문에 다른 두 객체는 결코 같은 해시코드를 가질 수 없다.
그런데 hash 값을 사용하는 Collection(HashMap, HashSet, HashTable ,, )은 객체가 논리적으로 같은지 비교할 때 위의 과정을 거친다. 일단 hashCode()의 리턴값에 따라 equals()를 호출할지 다른 객체라고 반환해버릴지 판단하는데, hashCode값이 일치해야 우리가 재정의한 equals()로 값을 비교할 수 있는 것이다. 따라서 hashCode도 오버라이딩해줘야 한다.
class Person{ long id; @Override public boolean equals(Object obj){ if(obj instanceof Person) //obj가 Object 타입이므로 id값 참조 위해 Person 타입으로 형변환. //주소가 아닌 값을 비교해서 같으면 true return id==((Person)obj).id; else //타입이 Person이 아님 비교할 필요도 없다. return false; } @Override public int hashCode(){ //Objects.hash() : 매개 값으로 주어진 값들을 이용해서 해시 코드를 생성하는 역할 //동일한 필드값을 가지는 객체는 동일한 해시코드를 가질 수 있다. return Objects.hash(id); } public Person(long id) { this.id = id; } }
따라서 hashCode()에 매개변수, 즉) 인자로 들어오는 값이 같으면 같은 hashCode를 만들도록 재정의 했더니 set에 두개의 다른 객체를 add했을 때 오버라이딩을 통해 하나만 저장되는 걸 확인할 수 있다.
반응형'프로그래밍 언어 > JAVA' 카테고리의 다른 글
[JAVA] 열거형(enum)이란? (0) 2022.10.24 [JAVA] TreeSet과 이진 탐색 트리 (1) 2022.10.15 [Servlet] HttpServletRequest, HttpServletResponse 파헤치기 (1) 2022.10.11 [JAVA ] 직렬화(Serialization)와 역직렬화(Deserialization) (0) 2022.10.11 [JAVA JUnit] assertThat의 isSameAs와 isEqaulTo 비교 분석 (1) 2022.10.01