일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Redisson
- 깃허브 뱃지
- maven 오류
- 레디스
- spring-boot-starter-parent
- 깃허브 방문자
- 파이썬 pip
- setting.xml
- Redis
- 윈도우10에 파이썬 설치
- 리드미 꾸미기
- 깃허브 리드미 꾸미기
- 프라이탁 파이썬
- 깃허브 프로필
- pip업그레이드
- 파이썬 설치
- 프라이탁
- 파이썬 설치하기
- 신규 프로젝트 생성후 빌드시 오류
- 깃허브
- Visual Studio Code 파이썬
- 분산락
- hashcode
- 알고리즘 입력받는 값
- 동등성
- 프라이탁 알림봇
- GitHub profile
- 깃허브 리드미
- Sanner와 BufferedReader의 차이점
- visual studio code
- Today
- Total
yeo72.devlog
[Java] 동일성과 동등성 본문
두 객체를 비교할 때 두 객체가 동일한지 아니면 동등한지에 대해서 비교 할 수있습니다.
그렇다면 동일성과 동등성이 무엇일까요?
두개의 이름이 같아서 헷갈리기 쉬운 개념이니 꼭 한번 정리하길 바랍니다.
동등성
두 객체가 동등하다는 것은 내용적으로 두 객체가 일치한다는 것을 의미합니다.
동일성
두 객체가 동일하다는 것은 객체의 메모리 주소가 동일하다는 것을 의미합니다.
자바에서 객체의 인스턴스를 생성할 경우 heap영역에 동적으로 메모리가 할당됩니다.
일반적으로 우리가 == 연산자로 객체를 비교할 경우에는 객체의 메모리 주소값을 비교하게 됩니다
따라서 객체의 값이 같아도 false가 나오게 됩니다.
그렇다면 euqals() 메서드는 어떨까요?
아래의 코드를 보고 확인해보겠습니다.
String testEquals1 = "테스트";
String testEquals2 = new String("테스트");
System.out.println("1번 : " + (testEquals1 == "테스트"));
System.out.println("2번 : " + (testEquals2 == "테스트"));
System.out.println("3번 : " + testEquals1.equals("테스트"));
System.out.println("4번 : " + testEquals2.equals("테스트"));
1, 2번은 == 연산자로 비교를 하고 3, 4번은 equals메서드를 통해 두 객체를 비교하였습니다.
위의 코드에서 1,2,3,4번은 어떤 값을 출력하게 될까요?

실제로 값을 출력해 보니 2번을 제외하고 모두 true가 나왔습니다.
String은 생성 할때마다 heap영역에 동적으로 메모리에 인스턴스를 생성하게 됩니다.
따라서 각 메모리의 주소값이 달라집니다.
그런데 왜 1번과 2번은 주소값을 비교하는 건데 왜 1번은 true가 나올까요?
바로 String Pool 때문입니다.
String Pool이란?
String Pool은 자바에서 문자열(String)을 효율적으로 관리하기 위한 메모리 영역으로,
불필요한 문자열 인스턴스의 중복 생성을 방지하고 메모리 사용을 최적화하는 역할을 합니다.
일반적으로 자주 사용되는 문자열은 String pool에 캐싱되어, 동일한 문자열을 여러 곳에서 사용할 때 매번 새로운 인스턴스를 생성하는 대신 기존에 있는 인스턴스를 재사용합니다.
문자열을 생성할 때에는 주로 String 리터럴을 사용하면 자동으로 String pool에서 검색하여
이미 존재하는 경우 해당 인스턴스를 반환하게 됩니다.
반면에 new
연산자를 사용하면 강제적으로 새로운 인스턴스를 생성하게 되므로
String pool에서의 재사용이 이뤄지지 않습니다.
그러므로, 문자열을 다룰 때는 주로 리터럴을 사용하거나 intern()
메소드를 활용하여 명시적으로
String pool에 추가하는 방법을 선호합니다.
이를 통해 문자열을 효율적으로 다루고 메모리 사용을 최적화할 수 있습니다.
String testEquals1 = "테스트";
다시 코드에서 해당 내용을 살펴보겠습니다.
testEquals1의 경우 리터럴로 선언된 문자열로 컴파일 시에 String Pool에 담기게 됩니다.
“테스트” 는 String Pool에서 검색되어 testEquals1을 재사용 하기 때문에 같은 객체 주소값을 바라보고 있습니다.
3, 4번의 경우 equals는 문자의 내용이 같으면 true를 반환하는 것을 확인할 수 있습니다.
이는 String이 Object의 equals를 오버라이딩 하여 객체의 내용이 같은지 확인하기 때문입니다.
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
String의 equals() 코드를 확인해보면 첫번째로 두 객체의 주소값이 같은지 확인합니다.
StringLatin1.equals(value, aString.value);
같지 않을 경우 위 코드에서 두 객체를 byte코드로 변환하여 값이 같은지 확인합니다.
equals메소드에 대해서 다시 한번 알아봅시다
앞서 말했듯이 equals는 모든 객체들의 조상인 Object에서 정의하고 있습니다.
그런데 Object에 있는 equals를 확인해보면 객체의 동일성을 기반으로 하고 있습니다.
public boolean equals(Object obj) {
return (this == obj);
}
즉, 두 참조변수가 동일한 객체를 가리키는지를 확인합니다.
이때문에 객체의 동등성을 비교하기 위해서는 equals 메서드를 오버라이딩 하여 내용적인 비교로 재정의 해주어야 합니다.
오버라이딩 하지않고 객체를 비교하는 경우에는 Object의 equals메서드가 호출되기 때문에 단순히 주소값만 비교하여 동등성을 보장하지 않습니다.
equals와 hashCode는 함께 재정의 해줘야 한다는데?
hashCode란?
해시함수로 생성되며 주어진 객체에 대한 일정 정수 값을 가집니다.
즉, 동일성을 보장합니다.
해시코드는 해시 기반의 컬렉션에서 필요로 합니다.
equals를 재정의 하려면 hashCode도 함께 재정의 해야 하는 이유는 equals를 오버라이딩 하여 객체를 비교하였을 경우 true가 나온다면 두 객체의 hashCode값 또한 같아야 하기 때문입니다.
왜 그럴까요?
그 이유는 앞서 말한 해시기반의 컬렉션 때문입니다. 해시기반의 컬렉션에서는 객체의 해시코드를 이용하여 빠른 검색과 저장을 하기 때문인데, 만약 두 객체가 논리적으로 동일하여 equals가 true라도 hashCode를 오버라이딩 하지 않으면 다른 정수가 나와서 해시기반의 컬렉션에서 다른 객체로 판단하는 불상사가 발생하게 됩니다.
정리
동일성(== 연산자)은 객체의 메모리 주소가 같은지를 비교합니다.
즉, 두 참조 변수가 동일한 객체를 가리키는지를 확인합니다.
동등성(equals 메서드)은 객체의 내용이 같은지를 비교합니다.
즉, equals 메서드가 내용적으로 동일하다고 판단하는 경우 두 객체는 동등합니다.
자바에서는 기본적으로 Object 클래스에서 상속받은 equals 메서드는 동일성을 기반으로 하며, 이는 == 연산자와 동일한 비교를 수행합니다.
따라서 객체의 내용적인 동등성을 비교하려면 equals 메서드를 해당 클래스에서 오버라이딩하여 내용 기반의 비교를 구현해야 합니다.
equals 메서드를 오버라이딩할 때는 반드시 hashCode 메서드도 함께 오버라이딩해야 합니다.
동일한 내용을 가진 객체들은 동일한 해시 코드를 가져야 하기 때문입니다.
이는 해시 기반의 컬렉션에서의 성능과 일관성을 유지하기 위함입니다.
'Study > Java' 카테고리의 다른 글
[JAVA] StringBuilder와 StringBuffer 비교 (1) | 2023.11.22 |
---|---|
[JAVA] 반복문 안에서 컬렉션 요소 변경하기 (0) | 2023.11.15 |
[Java] 메서드 디스패치란? (0) | 2023.11.07 |
ArrayList와 LinkedList는 언제 쓰면 좋을까? (0) | 2023.11.05 |
반복문(loop) 안에서 객체 생성과 성능 비교 (0) | 2023.10.18 |