💡 해당 글은 『자바의 신 3판』을 복습하며 도서의 내용과 본인의 주관적인 생각을 정리한 글입니다.
☁️ 내용정리
Object
자바에서 Object
란 모든 클래스의 부모가 되는 클래스이다. java.lang
패키지에 선언되어 있다.
클래스 계층 구조의 root에 해당한다. 모든 클래스는 Object가 될 수 있다.
Object 클래스에는 객체의 기본적인 행동을 정의해놓았다.
Object에 존재하는 메서드는 객체를 처리하기 위한 메서드와 스레드를 위한 메서드로 나눌 수 있다.
- protected Object
clone()
: 객체의 복사본을 만든다. - public boolean
equals(Object obj)
: 매개변수와 this가 논리적으로 동등한지 판단한다. - protected void
finalize()
: gc에 의해 호출되는 객체 수거 메서드이다. - public class<?>
getClass()
: 객체의 클래스 객체를 반환한다. - public int
hashCode()
: 객체의 해시코드를 반환한다. - public String
toString()
: 객체를 문자열로 표현하여 반환한다.
==과 equals()
==은 피연산자의 값을 비교하기 위해 사용된다, 이는 피연산자들이 물리적으로 동일한지를 판단한다. 피연산자가 모두 reference type인 경우 주소값을 비교한다.
equals()는 피연산자들이 논리적으로 동등한지를 판단하기 위해 사용된다. 재정의 하지 않는다면 ==과 동일한 기능을 구행한다.
public boolean equals(Object obj) {
return (this == obj);
}
equals는 아래의 규칙에 맞추어 재정의해야 한다.
- x.equals(x) == true
- a.equals(b) == b.equals(a)
- a.equals(a) == b.equals(c) == c.equals(a)
- 멱등성을 가져야 함.
- null에 대해선 항상 false
hashCode()
hashCode
는 기본적으로 객체의 메모리 주소를 기반으로 한 16진수 int타입을 반환한다.
hashCode가 같다고 해서 같은 객체인 것은 아니다. 해시 충돌의 가능성이 있기 때문이다. 즉 hashCode가 같다면 equals도 같다 라는 것은 거짓이다. hashCode가 다르다면 무조건 다른 객체인 것은 참이다.
반대로 equals가 true라면 hashCode는 동일해야 한다. 이런 결과가 나오지 않게 재정의했다면 언젠간 어디서든 사이드이펙트가 발생할 것이다.
☁️ 내 생각
equals() 재정의 시, hashCode()도 재정의해야 하는 이유
hash를 사용하는 컬렉션에서 오동작할 수 있는 가능성을 제거하기 위해서라고 생각한다.
HashMap의 내부 코드를 살펴보면 아래와 같이 동작하게 되어있다.
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { // ...
put 동작 시 먼저 key의 해시코드 값을 비교한다. 해시 값이 같으면 equals를 사용하여 동등한지 판단한다.
즉, hashCode 반환값이 다르다면 equals 메서드를 호출하기도 전에 다른 객체로 인식한다는 의미이다.
key를 통해 element를 찾는 작업도 동일하게 이루어진다.
그 이후, <K, V>를 가지는 Node의 배열 table변수에 hashCode()값을 인덱스로 사용하여 저장한다.
그렇다면 hashCode()는 어떻게 재정의해야 할까?
좋은 hashCode()는 어떻게 재정의해야 할까
좋은 hashCode()는 사용자의 코드에서 논리적 동등의 기준이 되는 상태를 가지고 만들어야 할 것이고, 다른 상태를 가진 인스턴스에 대해 최대한 중복 값이 발생하지 않아야 할 것이다.
다른 상태를 가진 인스턴스에 대해 같은 값을 반환하는 경우, 해시 충돌을 체이닝 방식으로 해결하는 HashMap의 탐색속도가 O(n)까지 증가할 수 있기 때문이다.
좋은 hashCode()를 작성하는 방법은 추후 이펙티브자바 정리 포스팅에서 다뤄볼 생각이다.
☁️ 질문
- toString()을 재정의하면 좋은 점
- 객체의 상태를 문자열로 표현할 수 있는 방법을 미리 구현 해놓기 때문에 특정 시점의 객체의 상태를 로깅하는 데 유용함
- == 과 equals()의 차이점
- ==은 두 객체 혹은 값이 물리적으로 동일한지 판단하는 연산이고, equals()는 두 객체가 논리적으로 동등한지 판단하는 방식이다.
- equals() 재정의 시, hashCode()도 재정의해야 하는 이유
- hash를 사용하는 컬렉션에서 오동작할 수 있는 가능성을 제거하기 위해서.
'JAVA > 자바의 신' 카테고리의 다른 글
15장. String (1) | 2024.01.07 |
---|---|
13장. 인터페이스와 추상클래스, enum (0) | 2024.01.05 |
11장. 매번 만들기 귀찮은데 누가 만들어 놓은 거 쓸 수 없나요? (0) | 2024.01.05 |
10장. 자바는 상속이라는 것이 있어요 (0) | 2024.01.05 |
9장. 자바를 배우면 패키지와 접근 제어자는 꼭 알아야 해요 (0) | 2024.01.05 |