equals()와 hashcode()
- 자바의 모든 객체는 Object클래스를 상속받고 있는데, equals()와 hashcode()는 Object클래스에 정의되어 있는 메소드이다. 때문에, 모든 객체는 equals()와 hashcode()를 상속받고 있다.
1. equals()
1) equals()란?
public boolean equals(Object obj) {
return (this == obj);
}
→ 일반적으로 Object클래스의 equals()는 위와 같이 정의되어 있다.
- 모든 객체는 위에 작성된 equals()를 상속받기에 반환하는 값인 “this == obj”를 보면 기본적으로 2개의 객체가 동일한지 검사하기 위해 사용된다고 볼 수 있다. 즉, 동일성(identity)을 비교하고 있다.
- 객체가 동일하다는 것은, 객체가 참조하는 것 즉 객체가 가리키는 곳인 메모리 주소가 같다는 것을 의미한다.
2) equals()의 Overriding
String test1 = new String("Test");
String test2 = new String("Test");
- 위 코드와 같이 같은 값을 가지고 있는 객체가 여러 개 생성된 경우가 있다. 두 객체는 값은 같지만, 서로 다른 메모리 주소를 가지고 있기에 동일한 객체가 아니다.
- 하지만, 프로그래밍을 할 때 메모리의 주소 비교보다는 같은 값을 지닌지의 여부를 확인하고 싶은 경우가 더 많다. 그래서 우리는 객체의 메모리 주소가 아닌 값으로 객체를 비교하도록 equals()를 Overriding 해줘야 한다. 즉, 동등성(equality)을 비교하도록 바꾸는 것이다.
System.out.println(test1 == test2); // false
System.out.println(test1.equals(test2)); // true;
→ 위 구문은 String 객체인 s1, s2를 ‘==’와 equals()를 이용하여 객체비교를 하고있다.
- 분명 String클래스는 Object를 상속받아 equals()를 사용하는 것인데, equals()의 내부를 봤을 때에는 ‘==’ 를 사용하고 있었다. 하지만 두 구문은 다른 결과 값을 반환하고 있다.
- 위 이유는 String클래스가 상속받은 equals()를 값 비교를 하도록 overriding하고 있기 때문이다.
- 우리가 만든 클래스도 equals()를 Overriding해서 값을 비교하게 할 수 있다.
class Person {
private static final String name;
public Person(String name) {
this.name = name;
}
}
→ 위 Person클래스는 생성자를 정의해주면서 인스턴스를 생성할 때 name을 초기화 해준다.
Person person1 = new Person("Kyu");
Person person2 = new Person("Kyu");
System.out.println(person1 == person2); //false
System.out.println(person1.equals(person2)); //false
- Person클래스로 두 개의 인스턴스를 생성해서 ‘==’와 equals()를 이용해 객체를 비교하면 String 클래스와는 다르게 둘 다 false를 반환한다. 이유는 두 개 모두 메모리 주소를 비교하고 있기 때문이다.
- equals()를 overriding 하여 name 값이 일치한지 바꿔야한다.
@Override
public boolean equals(Object obj) {
if(obj instanceof Person){
return this.name.equals(((Person)obj).name);
}
return false;
}
→ 위 코드로 Overriding하면
System.out.println(person1 == person2); //false
System.out.println(person1.equals(person2)); //true
→ 위의 결과 값이 나온다.
- equals() overriding 조건
- 재귀 : x.equals(x)
- 대칭 : x.equals(y), y.equals(x)
- 타동적 : x.equals(y), y.equals(z), x.equals(z)
- 일관 : x.equals(y) * n
- null과 비교 : x.equals(null)
2. hashcode()
1) hashcode()란?
public native int hashcode();
→ Object클래스에 hashcode는 위와 같이 정의되어 있다.
- 일반적으로 Object클래스의 hashcode()는 heap에 저장된 객체의 메모리 주소 값을 이용해여 hasing기법을 통해 해시코드를 만들어 반환한다. 주소 값으로 만든 고유한 숫자값이라고 볼 수 있다.
- hashcode는 HashTable과 같은 자료구조를 사용할 때 데이터가 저장되는 위치를 결정하기 위해 사용된다.
3. equals()와 hashcode()
- equals()를 overriding를 하여 동등한 두 객체가 놓여져 있다면, 우리는 같은 객체라고 생각할 것이다.
- 하지만 아래와 같이 우리가 HashSet자료구조를 사용한다고 가정하자.
public static void main(String[] args) {
Set<Person> Persons = new HashSet<>();
Persons.add(new Person("KO"));
Persons.add(new Person("KO"));
System.out.println(cars.size());
}
→ Set의 특성상 중복되는 것은 한번만 저장한다. 그렇다면 위의 출력 결과는 1이 나올 것이다. 하지만 결과는 2가 출력된다.
- 위와 같은 이유는 HashSet, HashMap, HashTable들은 hash값을 사용하기 때문이다.
- 위 세 개의 Collection은 객체가 논리적으로 같은지 비교할 때 hashcode()의 리턴 값이 일치하는지 1순위로 확인한다. 다음으로 equals()의 리턴 값이 true인지 확인한다. 두 가지 조건 모두 만족하면 동등 객체로 판단하는 것이다.
- 그래서 위 HashSet이 객체가 동등한지 판단할 때 equals()로 판단하기도 전에 hashcode()의 리턴값이 일치하지 않아 다른 객체로 판단한 것이다.
hashcode()의 overriding
- 위와 같은 문제때문에 equals를 overriding해주면 항상 hashcode()도 같이 overriding해줘야 한다.
@Override
public int hashcode() {
return Objects.hash(name); // name 필드의 해시코드를 반환한다.
}
→ Person클래스의 hashcode()를 overriding 한것이다.
→ Person클래스의 필드인 name의 해쉬코드를 반환하도록 재정의 한 것이다. name의 값이 같다면, 해쉬코드도 일치할 것이다.
hash를 이용한 Collection을 사용하지 않는다면?
- 물론 Hash를 이용한 Collection을 절대 사용하지 않을 자신이 있으면 hashcode()를 overriding을 하지 않아도 된다고 생각한다. 하지만 코드를 작성하다 보면 요구사항은 항상 변하기에 굳이 위험을 안으면서 overriding을 하지 않은 것은 바람직하지 않다고 본다. 그렇기에 내 생각은 equals()를 overriding해주면 항상 hashcode()를 overriding해줘야 한다고 생각한다.
[Reference]
'JAVA' 카테고리의 다른 글
Enum 조회 성능 향상하기(feat. Map) (1) | 2025.01.21 |
---|---|
Arrays.asList()와 List.of()의 차이 (0) | 2024.09.30 |
stream.toList() 와 .collect(Collectors.toList()) 차이 (0) | 2024.09.30 |
Enum (1) | 2023.09.15 |