StringBuilder가 equals(), hashcode()를 재정의하지 않는 이유, StringBuilder의 동등성 비교

Yuri JI·2023년 5월 7일
3

TIL

목록 보기
10/10
post-thumbnail

📌 코딩테스트를 풀던 중 StringBuilder 객체를 equals로 비교했는데 자꾸만 다른 값이 나왔다. 분명 자바의 정석에서 StringBuilder의 동등성 비교에 대해서 공부했는데 잊은 것 같아서 다시 정리 해보고자 했다.

👀 동일성? 동등성?

  • 동일성(Identity) : 객체가 참조하는 것이 동일한지를 확인한다. 즉, 객체가 동일한 메모리 주소를 가리키는 경우 동일한 객체가 된다.
  • 동등성(Equlity) : 같은 값을 가진 객체인지 확인한다.

👀 equals 메소드

동일성(Identity)를 비교하는 메소드이다. 두 객체가 동일한 메모리 주소를 가리키는지를 확인한다.

/* Object 클래스의 equals 메서드 */
 public boolean equals(Object obj) {
        return (this == obj);
}

👀 hashCode 메소드

실행 중(Runtime)에 객체의 유일한 integer 값을 반환한다. Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환하도록 되어있다. HashTable, HashMap, HashSet과 같은 자료구조를 사용할 때 데이터가 저장되는 위치를 결정하기 위해 사용된다.

public native int hashCode();

📗 equals와 hashCode의 관계

  • Java 프로그램을 실행하는 동안 equals에 사용된 정보가 수정되지 않았다면, hashCode는 항상 동일한 정수값을 반환해야 한다. (Java의 프로그램을 실행할 때 마다 달라지는 것은 상관이 없다.)
  • 두 객체가 equals()에 의해 동일하다면, 두 객체의 hashCode() 값도 일치해야 한다.
  • 두 객체가 equals()에 의해 동일하지 않다면, 두 객체의 hashCode() 값은 일치하지 않아도 된다.

-> 동일한 객체는 동일한 메모리 주소를 갖는다는 것으로, 동일한 해시코드를 가져야한다.
-> 결국 equals 메소를 오버라이드 하면, hashCode 메소드도 함께 오버라이드 해야한다.

📗 String의 동등성(Equality) 비교

String의 경우 Object 클래스의 equals 메소드와 hashCode 메소드가 오버라이드 되어 있다.
그래서 String 클래스는 equals()를 이용하여 동등성(Equality)을 비교할 수 있다.

String str1 = new String("Good Morning~");
String str2 = new String("Good Morning~");

System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true

String 클래스의 equals 메소드를 보면 첫 번째 if문에서 주소 값이 같은 객체라면 바로 true 값을 반환한다. 여기에서 동일한 객체는 동등한 객체임을 알 수 있다.

/* String 클래스의 equals 메소드 */ 
public boolean equals(Object anObject) {
    if (this == anObject) { // 동일한 객체(주소 값이 같으면 true를 return
        return true;
    }
    if (anObject instanceof String) { // 문자열의 문자를 하나씩 비교
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

/* String 클래스의 hashCode 메소드 */ 
public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            hash = h = isLatin1() ? StringLatin1.hashCode(value)
                                  : StringUTF16.hashCode(value);
        }
        return h;
    }

📕 StringBuilder의 동등성 비교

StringBuilder는 String과 달리 equals 메소드와 hashCode 메소드를 오버라이드 하지 않았다. 따라서 StringBuilder의 동등성을 비교하기 위해서는 StringBuilder 객체를 toString()을 이용해 String으로 변환한 후 equals 메소드를 사용하면 된다.

📕 StringBuilder가 equals(), hashcode()를 재정의하지 않는 이유

StringBuilder의 의도된 용도는 시간이 지남 에 따라 변경될 수 있는 문자 버퍼를 유지 관리하는 것으로, mutable한 객체이다.

  • hashCode()를 재정의 하지 않는 이유
    해시 알고리즘을 사용하는 데이터 구조에서 Key로 사용되는 객체를 수정하면 저장된 값이 손실될 가능성이 있다. 따라서 StringBuilder는 변경 가능한 객체(mutable)에 대해 hashCode()를 재정의하는 것은 일반적으로 유용하지 않다.

  • equals()를 재정의 하지 않는 이유
    두 객체가 equals 메서드로 같으면 해시 코드가 동일해야 한다. 즉, equals 메서드가 재정의된 경우 해당 hashCode 메서드도 재정의되어야 한다. 그러나 위의 이유처럼 고유한 해시코드 구현을 하지 않아서 equals 메서드도 재정의하지 않는다.

+) StringBuffer의 동등성 비교

StringBuffer와 StringBuilder는 동기화라는 한 가지 차이점을 제외하고 동일한 메서드를 가진다. 따라서 StringBuffer의 동등성 비교는 StringBuilder의 동등성 비교 방법과 동일하다.

StringBuffer는 동기화가 되지만, StringBuilder는 동기화를 지원하지 않는다.
즉, StringBuffer는 스레드로부터 안전하고, StringBuilder는 스레드로부터 안전하지 않다.

📍 참고한 사이트

https://getinterviewinfo.wordpress.com/2014/09/16/why-stringbuffer-and-stringbuilder-doesnt-override-equals-and-hashcode-method/
https://mangkyu.tistory.com/101

profile
안녕하세요 😄

5개의 댓글

comment-user-thumbnail
2023년 5월 8일

StringBuilder에 equals와 hashCode가 없는지 처음 인지했네요!
역시 자바 개발자들은 다 깊은 뜻이 있군요~~ 유익한 글 잘 읽었습니다.

답글 달기
comment-user-thumbnail
2023년 5월 8일

StringBuilder에 equals와 hashCode가 없는지 처음 인지했네요!
역시 자바 개발자들은 다 깊은 뜻이 있군요~~ 유익한 글 잘 읽었습니다.

답글 달기
comment-user-thumbnail
2023년 5월 8일

StringBuilder에 equals와 hashCode가 없는지 처음 인지했네요!
역시 자바 개발자들은 다 깊은 뜻이 있군요~~ 유익한 글 잘 읽었습니다.

답글 달기
comment-user-thumbnail
2023년 5월 8일

StringBuilder가 Object의 Equals()와 Hashcode()를 재정의 하지 않은 지는 몰랐네요.
항상 동등성 비교를 위해서는 toString()을 해야한다는 점을 잊지 않아야겠어요.
StrinbBuilder와 StringBuffer가 스레드안전 차이가 있는것은 아는데 그 차이가 정확히 무엇을 의미하는지 추가 포스팅이 있으면 좋을 것 같습니다. 좋은글 감사하니다

답글 달기
comment-user-thumbnail
2023년 5월 8일

StringBuilder의 동등성을 정리하면서 String의 동등성과 비교해서 설명해주셔서 이해가 수월했습니다!
StringBuilder가 왜 equals()와 hashcode()를 제공해주지 않는지에 대한 이유도 풀어주셔서 좋았습니다!!

답글 달기