Welcome! Everything is fine.

[Java] 동등성과 동일성, equals() 메서드 파헤치기 본문

Java

[Java] 동등성과 동일성, equals() 메서드 파헤치기

개발곰발 2024. 11. 13.
728x90

자바에서 두 객체가 같다는걸 확인할 때, 두 가지로 표현할 수 있다는 것을 배웠다.

  • 동일성(Identity) : == 연산자 이용, 두 객체의 참조가 동일한 객체를 가리키는지 확인
  • 동등성(Equality) : equals() 메서드 이용, 두 객체가 논리적으로 동등한지 확인

강의를 보며 예제 코드로 실습을 하다가 이해가 가지 않는 부분이 있었다. User 객체를 생성해 동일성, 동등성 비교를 할 때, 모두 false가 출력된 것이다. 그리고 Object가 기본적으로 제공하는 equals()는 == 으로  동일성 비교를 제공한다는 것이었다. 그래서 '그럼 왜 euqlas()'라는 메서드를 만든거지? 라는 생각이 들었다.

 User user1 = new User("id-100");
 User user2 = new User("id-100");

 System.out.println("identity= " + (user1 == user2));
 System.out.println("identity= " + (user1.equals(user2)));
identity= false
equality= false

 

실제로 Object 클래스에 들어가 equals() 메서드를 보니 다음과 같이 되어있었다. 그럼 문자열을 비교할 때는 동등성과 동일성이 잘 비교되었는데, 왜 객체를 비교할 때는 비교가 안될까?

 

바로 동등성의 개념이 각각의 클래스마다 다르기 때문이다. User의 동등성은 아이디로 처리할 수도 있고, 주민등록번호, 혹은 전화번호로 처리할 수 있다. 그래서 단순히 User라는 객체만 가지고 비교를 한다면? 동등성 비교를 할 수 없다.


 

더 찾아보니, String 클래스의 equals() 메서드는 단순히 == 으로 비교하지 않는다. 내가 생각하는 이상적인 동등성 비교를 하려면 String 클래스에 오버라이딩된 equals() 메서드를 쓰면 되는 것이었다! 그렇다면 String 클래스로 들어가 equals() 메서드를 보자.

 

맨 처음 if문부터 살펴보면, this와 anObject가 동일한 참조인지 확인해서 같으면 true를 반환한다. 여기서 this는 equals() 메서드가 호출된 현재 String 객체를 의미한다.

if (this == anObject) {
    return true;
}

 

그 다음으로는 anObject가 String 타입인지 확인한다. 다음 조건과 &&로 이어져 있기 때문에 String 타입이 아니면 바로 false가 반환된다.

return (anObject instanceof String aString)

 

앞의 조건을 통과했다면, COMPACT_STRINGS가 활성화된 경우, this와 aString의 coder 값이 같은지 확인한다. COMPACT_STRINGS가 무엇을 의미하는지 찾아보니 Java 9부터 도입된 문자열 메모리 최적화 기능의 일부로, 문자열을 메모리 효율적으로 관리하기 위해 사용된다고 한다. ASCII 문자열이면 1바이트(UTF-8) 인코딩, 그렇지 않으면 2바이트(UTF-16)로 인코딩되도록 최적화하는 기능이다.

&& (!COMPACT_STRINGS || this.coder == aString.coder)

 

그래서 COMPACT_STRINGS가 활성화 되어있다면,

  • 두 문자열의 coder 값(각 문자열의 인코딩 방식)을 비교하여 같은 인코딩 방식인지 비교한다.
    • coder 값이 같으면(=인코딩 방식이 같으면) 바이트 배열을 바로 비교할 수 있다.
    • corder 값이 다르면(=인코딩 방식이 다르면) 비교를 진행하지 않고 불필요한 메모리 접근과 연산을 방지한다.

마지막으로, 두 문자열의 내용을 비교한다. 문자열 내용이 동일하면 true를, 그렇지 않으면 false를 반환한다.

StringLatin1.equals(value, aString.value);

 

 

StringLatin1 클래스에 들어가보니 다음과 같은 구조였다. 두 개의 byte 배열의 길이가 같은지 먼저 확인 후, 길이가 같으면 배열의 각 요소를 하나씩 비교하면서 모든 요소가 같을 경우에만 true를 반환한다.

 

이렇게 궁금한 점을 직접 코드를 보고 해석하며 해결해보았다! 그냥 대충 넘어가기보다 이렇게 하나씩 정리하니 더 이해도 잘 가고 기억에 오래 남을 것 같다.