동등성 과 동일성? == 와 equals?
동등성(Equality) vs 동일성(Identity) 개념 정리
동일성 (Identity)
- 동일성이란 두 객체가 같은 메모리 주소(참조값)를 가리키고 있는지를 의미합니다.
- 즉, 동일한 객체인지 확인하는 것을 말합니다.
- Java에서는 == 연산자를 사용하여 동일성을 비교합니다.
동등성 (Equality)
- 동등성이란 두 객체의 "내용(값)"이 같은지를 비교하는 것을 의미합니다.
- 즉, 값이 같다면 "같은 객체"로 간주할 수 있도록 하는 개념입니다.
- Java에서는 equals() 메서드를 사용하여 동등성을 비교합니다.
- Java에서 equals() 메서드는 기본적으로 ==과 동일한 방식으로 동작한다.하지만 equals()를 오버라이딩하면 "동등성(Equality)"을 비교하도록 변경할 수 있다.
- 즉, 오버라이딩하지 않으면 equals()는 동일성(Identity) 비교를 수행하며, ==과 같은 결과를 반환한다.
- equals()를 오버라이딩하면 "값"을 기준으로 객체 비교를 수행할 수 있습니다.
== vs equals() 차이점
비교 대상 == (동일성 비교) equals() (동등성 비교)
비교 기준 | 객체의 참조값(메모리 주소) 비교 | 객체의 내용(값) 비교 |
기본 동작 | 같은 주소를 가리키면 true | Object의 기본 구현은 ==과 동일하지만, 오버라이딩하면 값 비교 가능 |
오버라이딩 가능 여부 | ❌ 불가능 (연산자) | ✅ 가능 (equals() 메서드 오버라이딩 가능) |
사용 예 | 기본 자료형 비교 또는 객체가 같은 주소를 가리키는지 확인할 때 | 객체의 값이 같은지 확인할 때 |
동일성과 동등성 예제 코드
동일성 비교 (==)
public class Test {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false (서로 다른 객체)
System.out.println(s1.equals(s2)); // true (값이 같음)
}
}
✔ 출력 결과
false
true
결론:
✔ == 연산자는 객체의 메모리 주소를 비교하기 때문에 false
✔ equals()는 객체의 "값"을 비교하기 때문에 true
동등성 비교 (equals() 오버라이딩)
📌 equals()를 오버라이딩하면 "값"이 같으면 같은 객체로 판단 가능!
import java.util.Objects;
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("홍길동", 30);
Person p2 = new Person("홍길동", 30);
Person p3 = p1; // p3는 p1과 같은 객체 (주소값 같음)
System.out.println(p1 == p2); // false (동일성 X)
System.out.println(p1.equals(p2)); // true (동등성 O)
System.out.println(p1 == p3); // true (동일성 O)
}
}
✔ 출력 결과
false
true
true
결론:
✔ p1 == p2 → 서로 다른 객체이므로 false
✔ p1.equals(p2) → 값이 같아서 true
✔ p1 == p3 → 같은 객체이므로 true
hashCode()란?
✔ hashCode()는 객체를 식별하는 정수 값(해시 값)을 반환하는 메서드
✔ Java의 Object 클래스에서 기본적으로 제공하는 메서드
✔ 객체를 HashSet, HashMap, HashTable 등에서 빠르게 검색하기 위해 사용됨
✔ 기본적으로 Object의 hashCode()는 객체의 메모리 주소를 기반으로 생성됨
📌 Java의 Object 클래스에서 hashCode() 기본 구현
public native int hashCode(); // 기본적으로 객체의 주소값 기반 해시코드 반환
hashCode()와의 관계
✔ 객체가 같다면 (equals()가 true라면) hashCode()도 같아야 한다.
✔ 하지만 hashCode()가 같다고 해서 equals()가 true라는 보장은 없다.
✔ 즉, 같은 객체로 판단(equals()가 true)되면, hashCode()도 같아야 한다.
✔ 그러나 서로 다른 객체라도 같은 hashCode()를 가질 수 있다. (해시 충돌 가능)
import java.util.Objects;
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
@Override
public int hashCode() { // 해시코드 오버라이딩
return Objects.hash(name, age);
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("홍길동", 30);
Person p2 = new Person("홍길동", 30);
System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode()); // 같은 해시코드 반환
System.out.println(p2.hashCode());
}
}
✔ 출력 결과: equals()를 오버라이딩하면 hashCode()도 같아야 한다!
true
12345678 // (예제 해시코드)
12345678
hashCode()를 오버라이딩하는 이유
✔ 컬렉션(HashSet, HashMap)에서 객체를 효율적으로 저장하고 검색하기 위해 필요
✔ equals()를 오버라이딩하면 hashCode()도 함께 오버라이딩해야 함
✔ 그렇지 않으면, HashSet에서 중복 객체가 허용되는 문제 발생 가능
📌 예제: equals()만 오버라이딩하면 발생하는 문제
import java.util.HashSet;
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) { // equals 오버라이딩
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
// hashCode() 미오버라이딩 → 기본적으로 Object의 hashCode() 사용
}
public class Test {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
Person p1 = new Person("홍길동", 30);
Person p2 = new Person("홍길동", 30);
set.add(p1);
set.add(p2); // 서로 다른 객체로 인식됨
System.out.println(set.size()); // 2 (중복 저장됨)
}
}
✔ 출력 결과: 값이 같아도 hashCode()가 다르면 HashSet에서 다른 객체로 인식됨!
2
hashCode()가 같아도 equals()가 다를 수 있음 (해시 충돌)
📌 서로 다른 객체가 같은 hashCode()를 가질 수 있음 (Hash Collision).
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() { // 단순한 해시코드 (충돌 발생 가능)
return age; // 나이만 기준으로 해시코드 생성
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("홍길동", 30);
Person p2 = new Person("이순신", 30); // 다른 객체지만 해시코드 같음
System.out.println(p1.hashCode()); // 30
System.out.println(p2.hashCode()); // 30 (해시 충돌 발생)
System.out.println(p1.equals(p2)); // false (값이 다름)
}
}
✔ 출력 결과: hashCode()가 같아도 equals()가 false일 수 있다!
30
30
false