add user agent parser and update build configuration
This commit is contained in:
163
docs/EqualsAndHashCode.md
Normal file
163
docs/EqualsAndHashCode.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# **자바의 `equals()` 와 `hashCode()` 메서드 완벽 정리**
|
||||
|
||||
## **1. `equals()` 와 `hashCode()`란?**
|
||||
자바에서 객체를 비교할 때, `==` 연산자는 **메모리 주소를 비교**한다.
|
||||
하지만 **객체의 내용이 같은지를 비교**하려면 `equals()`를 재정의해야 한다.
|
||||
|
||||
또한, **HashMap, HashSet 같은 컬렉션에서 객체를 올바르게 저장하고 검색하려면 `hashCode()`도 재정의**해야 한다.
|
||||
|
||||
---
|
||||
|
||||
## **2. `equals()` 와 `hashCode()`의 기본 동작**
|
||||
### ✅ **1. `equals()` 기본 구현 (`Object` 클래스)**
|
||||
모든 클래스는 `Object` 클래스를 상속받으며, `Object`의 기본 `equals()`는 **메모리 주소를 비교**한다.
|
||||
```java
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj);
|
||||
}
|
||||
```
|
||||
즉, 기본적으로 `equals()`는 `==`과 동일한 동작을 한다.
|
||||
|
||||
### ✅ **2. `hashCode()` 기본 구현 (`Object` 클래스)**
|
||||
`Object`의 `hashCode()`는 **객체의 메모리 주소를 기반으로 해시 값을 반환**한다.
|
||||
```java
|
||||
public native int hashCode();
|
||||
```
|
||||
(※ `native`는 자바가 아닌 C/C++로 구현되었다는 의미)
|
||||
|
||||
---
|
||||
|
||||
## **3. `equals()`와 `hashCode()`를 함께 재정의해야 하는 이유**
|
||||
**예제:** `HashSet`에서 `equals()`만 재정의했을 때 발생하는 문제
|
||||
```java
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
class Person {
|
||||
String name;
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Person person = (Person) obj;
|
||||
return Objects.equals(name, person.name);
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
HashSet<Person> set = new HashSet<>();
|
||||
set.add(new Person("Alice"));
|
||||
System.out.println(set.contains(new Person("Alice"))); // false (올바르게 동작하지 않음!)
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ 이유: `HashSet`은 **`hashCode()`를 먼저 비교한 후 `equals()`를 실행**하는데, `hashCode()`가 재정의되지 않아서 `contains()`가 실패한다.
|
||||
|
||||
✅ 해결 방법: `hashCode()`를 `equals()`와 함께 재정의한다.
|
||||
|
||||
---
|
||||
|
||||
## **4. `equals()`와 `hashCode()` 올바르게 재정의하는 방법**
|
||||
### ✅ **올바른 `equals()` 재정의**
|
||||
1. **반사성** (`x.equals(x) == true`)
|
||||
2. **대칭성** (`x.equals(y) == true` 면 `y.equals(x) == true`)
|
||||
3. **추이성** (`x.equals(y) == true` & `y.equals(z) == true` 면 `x.equals(z) == true`)
|
||||
4. **일관성** (`x.equals(y)`의 결과가 변하지 않음)
|
||||
5. **null 비교 시 `false` 반환** (`x.equals(null) == false`)
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Person person = (Person) obj;
|
||||
return Objects.equals(name, person.name);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ **올바른 `hashCode()` 재정의**
|
||||
✔ **같은 객체는 같은 `hashCode()`를 가져야 한다.**
|
||||
✔ **다른 객체라도 같은 `hashCode()`를 가질 수는 있지만 가능하면 충돌을 줄이는 것이 좋다.**
|
||||
|
||||
**예제 코드:**
|
||||
```java
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name);
|
||||
}
|
||||
```
|
||||
`Objects.hash()`는 `null` 체크까지 포함된 안전한 방식이다.
|
||||
|
||||
---
|
||||
|
||||
## **5. `equals()`와 `hashCode()`를 올바르게 구현한 예제**
|
||||
```java
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
class Person {
|
||||
String name;
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Person person = (Person) obj;
|
||||
return Objects.equals(name, person.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name);
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
HashSet<Person> set = new HashSet<>();
|
||||
set.add(new Person("Alice"));
|
||||
System.out.println(set.contains(new Person("Alice"))); // true (올바르게 동작!)
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ `equals()`와 `hashCode()`를 함께 재정의했기 때문에 `contains()`가 정상적으로 동작한다.
|
||||
|
||||
---
|
||||
|
||||
## **6. `record`를 사용하면 자동 구현됨! (Java 14+)**
|
||||
자바 14부터 도입된 `record`는 `equals()`와 `hashCode()`를 자동 생성해준다.
|
||||
|
||||
```java
|
||||
record Person(String name) {}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
HashSet<Person> set = new HashSet<>();
|
||||
set.add(new Person("Alice"));
|
||||
System.out.println(set.contains(new Person("Alice"))); // true
|
||||
}
|
||||
}
|
||||
```
|
||||
✔ `record`는 **자동으로 `equals()`와 `hashCode()`를 올바르게 구현**해준다.
|
||||
|
||||
---
|
||||
|
||||
## **7. 결론**
|
||||
- **`equals()`만 재정의하면 HashSet, HashMap 등의 컬렉션에서 문제가 발생할 수 있다.**
|
||||
- **`hashCode()`도 함께 재정의해야 컬렉션에서 정상 동작한다.**
|
||||
- **`record`를 사용하면 자동으로 `equals()`와 `hashCode()`가 구현된다.**
|
||||
|
||||
이제 `equals()`와 `hashCode()`를 제대로 이해했으니, 컬렉션을 사용할 때 예상치 못한 버그를 피할 수 있을 것이다!
|
||||
Reference in New Issue
Block a user