equals(Object obj)
- Object 클래스가 가지고 있는 equals 메서드는 객체의 참조변수를 받아서 비교하여 그 결과를 boolean으로 알려주는 역할을 한다.
equals 메서드의 실제 코드
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
}
위에 코드에서 알 수 있듯이 두 객체의 같고 다름을 참조변수 값으로 판단한다
그렇기 때문에 서로 다른 두 객체를 equals 메서드로 비교하면 항상 false 인 결과로 얻게 된다.
public class Test {
public static void main(String[] args) {
MemberDTO m1 = new MemberDTO("홍길동");
MemberDTO m2 = new MemberDTO("홍길동");
if(m1.equals(m2)) System.out.println("m1 , m2 is same");
else System.out.println("m1 , m2 is dffrent");
System.out.println(m1);
System.out.println(m2);
m1 = m2;
if(m1.equals(m2)) System.out.println("m1 , m2 is same");
else System.out.println("m1 , m2 is dffrent");
System.out.println(m1);
System.out.println(m2);
// m1 , m2 is dffrent
// hello.core.godofjava.MemberDTO@54bedef2
// hello.core.godofjava.MemberDTO@5caf905d
// m1 , m2 is same
// hello.core.godofjava.MemberDTO@5caf905d
// hello.core.godofjava.MemberDTO@5caf905d
}
}
- 두 개의 MemberDto 클래스의 인스턴스를 생성한 다음 equals를 이용해서 두 인스턴스를 비교해 보았다.
- equals 메서드는 주소값을 비교하기 때문에, 두 MemberDto 인스턴스의 name 값이 홍길동으로 같을지라도 equals메서드로 비교한 결과는 false 일 수밖에 없다.
하지만 "m1 = m2;"을 수행한 후에는 참조변수 m1 은 m2 가 참조하고 있는 인스턴스의 주소값이 저장되므로 m1 도 m2와 같은 주소값이 저장된다.
그래서 이번에는 m1.equals(m2) 결과가 true 가 되는 것이다.
주소값이 아니라 value 값을 비교할 수는 없을까?
equals를 오버라이딩해서 사용하면 가능하다.
import java.util.Objects;
public class MemberDTO {
String name;
public MemberDTO(String name){
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MemberDTO memberDTO = (MemberDTO) o;
return Objects.equals(name, memberDTO.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
equals 메서드가 MemberDto 인스턴스의 주소값이 아닌 memberDto의 같은 name 값을 가지고 있다면 equals 메서드를 비교했을 때 true 값의 결과를 얻을 수 있도록 할 수 있다.
equals를 재정의하려면 hashCode 도 재정의 해야 한다는 규약이 있다.
hashCode를 재정의하지 않으면 어떻게 될까?
해싱이란 해시함수(hash function)를 이용해서 데이터를 해시테이블에 저장하고 검색하는 기법을 말한다. 해시함수는 데이터가 저장되어 있는 곳을 알려주기 때문에 다량의 데이터 중에서도 원하는 데이터를 빠르게 찾을 수 있다.
해싱을 구현한 컬렉션 클래스로는 HashSet, HashMap, Hashtable 등이 있다. Hashtable 은 컬렉션 프레임웍이 도입되면서 HashMap으로 대체되었으나 이전 소스와 호환성 문제로 남겨두고 있다. 가능하면 Hashtable 대신 HashMap을 사용하도록 하자.
HashMap, HashSet 과 같이 해싱을 구현한 컬렉션 클래스에서 equals()의 호출결과가 true 지만 해시코드가 다른 두 객체를 서로 다른 것으로 인식하고 따로 저장할 것이다.
equals 또는 hashCode만 재정의할 경우
1. hashCode만 재정의하고 equals를 재정의하지 않을 때:
같은 해시 코드로 동일한 버킷에 접근하더라도, equals 비교에서 객체의 메모리 주소값을 비교하게 되어 중복된 객체로 간주될 수 있다. 논리적으로 동일한 객체라도 중복 저장이 발생할 수 있다.
2. equals만 재정의하고 hashCode를 재정의하지 않을 때:
HashMap이나 HashSet에서 논리적으로 동일한 객체를 같은 버킷에 저장하지 못해, 검색 속도가 저하될 수 있다.
equals와 hashCode를 재정의할 필요가 없는 경우
LinkedList처럼 논리적인 동치성을 검사하지 않는 자료구조에서는 equals와 hashCode를 재정의하지 않아도 된다.
그러나 HashSet, HashMap과 같은 해싱 기반 자료구조를 사용할 때는 반드시 두 메서드를 재정의해야 한다.
String 클래스 역시 Obkect 클래스의 equals 메서드를 그대로 사용하는 것이 아니라 이처럼 오버라이딩해서 String 인스턴스가 갖는 문자열을 비교하도록 되어있다. 이렇기 때문에 같은 내용의 문자열을 갖는 두 String 인스턴스에 equals 메서드를 사용하면 항상 true 값을 갖는 것이다.
객체의 고유값을 나타내는 hashCode()
hashCode는 직접 구현할 일은 거의 없다.
hashCode 메서드는 기본적으로 객체의 메모리 주소를 16 진수로 리턴한다.
만약 어떤 두 개의 객체가 서로 동일하다면 hashCode 값도 동일해야만 한다.
따라서 equals 메서드를 override 하면, hashCode 메서드도 overriede 해서 동일한 결과가 나오도록 해야 한다
등가 비교연산자
== 는 두 피연자의 값이 같은지를 확인하는 연산자입니다. 기본형은 물론 참조형, 즉 모든 자료형에 사용할 수 있습니다.
기본형의 경우 변수의 저장되어 있는 값이 같은지를 알 수 있고 참조형의 경우 객체의 주소값을 저장하기 때문에 두 피연자의 주소값이 같은지를 알 수 있습니다.
.
참고 자료 : 자바의 정석, 자바의 신
'JAVA' 카테고리의 다른 글
[JAVA] Error 와 Exception (0) | 2024.11.01 |
---|---|
[JAVA] staic 과 final (1) | 2024.10.29 |
[Java] Call by Value 와 Call by Reference (6) | 2024.10.21 |
[자바의 신] 정리해봅시다 [1장~ 10장] (0) | 2024.10.19 |
[JAVA] ORM 이란? MyBatis 와 JPA 차이 (4) | 2024.10.11 |