본문 바로가기
개발공부/개념정리

[Java] String, StringBuffer, StringBuilder

by parkman

1. Java 문자열 대표 클래스

Java 에서 문자열을 다루는 대표적인 클래스는 String, StringBuilder, StringBuffer 가 있습니다.

3가지 클래스는 각자의 차이점이 존재합니다.

 

Java에서 String 객체는 한번 값이 할당되면 그 공간은 변하지 않습니다.

하지만 Stringbuilder StringBuffer 객체는 한번 값이 할당되더라도 한번 더 다른 값이 할당되면 할당된 공간이 변하는 특성을 갖고 있습니다.

 

String과 StringBuffer와 StringBuilder의 기본적인 차이는

String불변(Immutable)성,

StringBuffer와 StringBuilder가변(Mutable)성 이다.

 

직관적이어서 가장 많이 사용할 듯한 위의 예제에서 "hello" 값을 가지고 있던 String 클래스의 참조변수 str이 가리키는 곳에 저장된 "hello"에 "world" 문자열을 더해 "hello world"로 변경한 것으로 착각할 수 있습니다.

	String str1 = "hello";
        String str2 = new String("hello");

        String str5 = str1 + " world";
        System.out.println(str5); // [ hello world ]
        System.out.println(str5==str1); //false
        
        String str6 = str2 + " world";
        System.out.println(str6); // [ hello world ]
        System.out.println(str6==str2); //false
하지만 기존에 "hello" 값이 들어가있던 String 클래스의 참조변수 str이 "hello world"라는 값을 가지고 있는 새로운 메모리영역을 가리키게 변경되고 String 클래스는 불변하기 때문에 문자열을 수정하는 시점에 새로운 String 인스턴스가 생성된 것이다.

 
위와 같이 String은 불변성을 가지기 때문에 변하지 않는 문자열을 자주 읽어들이는 경우 String을 사용해 주시면 좋은 성능을 기대할 수 있습니다. 그러나 문자열 추가,수정,삭제 등의 연산이 빈번하게 발생하는 알고리즘에 String 클래스를 사용하면 힙 메모리(Heap)에 많은 임시 가비지(Garbage)가 생성되어 힙메모리가 부족으로 어플리케이션 성능에 치명적인 영향을 끼치게 됩니다.

 

이를 해결하기 위해 Java에서는 가변(mutable)성을 가지는 StringBuffer / StringBuilder 클래스를 도입했습니다.
 
String 과는 반대로 StringBuffer/StringBuilder 는 가변성 가지기 때문에 .append() .delete() 등의 API를 이용하여 동일 객체내에서 문자열을 변경하는 것이 가능합니다. 따라서 문자열의 추가,수정,삭제가 빈번하게 발생할 경우라면 String 클래스가 아닌 StringBuffer/StringBuilder를 사용해야 합니다.
        StringBuffer str7 = new StringBuffer("hello");
        StringBuffer str8 = str7.append(" world");
        System.out.println(str8);  // [ hello world ]
        System.out.println(str7==str8); //true
        
        StringBuilder str9 = new StringBuilder("hello");
        StringBuilder str10 = str9.append(" world");
        System.out.println(str10);  // [ hello world ]
        System.out.println(str9==str10); //true

2. StringBuffer, StringBuilder

그렇다면 동일한 API를 가지고 있는 StringBufferStringBuilder의 차이점은 무엇일까요?
가장 큰 차이점은 동기화의 유무로써
StringBuffer는 동기화를 지원하여 멀티쓰레드 환경에서 안전하다는 점(thread-safe) 입니다. 
StringBuilder는 동기화를 지원하지 않기때문에 멀티쓰레드 환경에서 사용하는 것은 적합하지 않지만 동기화를 고려하지 않는 만큼 단일쓰레드에서의 성능은 StringBuffer 보다 뛰어납니다.

* 참고로 String 불변성을 가지기때문에 마찬가지로  멀티쓰레드 환경에서의 안정성(thread-safe)을 가지고 있습니다. 

3. 일반적인 사용용도

  • String               :  문자열 연산이 적고 멀티쓰레드 환경일 경우
  • StringBuffer     :  문자열 연산이 많고 멀티쓰레드 환경일 경우(동기화 지원)
  • StringBuilder   :  문자열 연산이 많고 단일쓰레드이거나 동기화를 고려하지 않아도 되는 경우

 

면접질문!

String, StringBuffer, StringBuilder 차이점이 무엇인가요?

  • String               :  객체의 값이 할당되면 할당된 공간이 변하지 않는 불변성인 특성이 있습니다.
    * 불변성을 가지기때문에 마찬가지로  멀티쓰레드 환경에서의 안정성(thread-safe)을 가지고 있습니다. 
  • StringBuilder, StringBuffer객체는 한번 값이 할당되더라도 한번 더 다른 값이 할당되면
                                                 할당된 공간이 변하는 가변성인 특성이 공통적으로 있으나 차이점이 존재합니다.
                                                 차이점으로는 동기화의 유무 입니다.
  • StringBuffer     :  동기화를 지원하기 때문에 멀티쓰레드 환경에서 안전성을 가지고 있습니다.(Synchronized)
  • StringBuilder   :  동기화를 지원하지 않아 멀티쓰레드 환경보다는 단일 쓰레드 환경에서는
                               buffer보다 성능이 우수합니다.

 * Synchronized란?

 - 공유 데이터에 lock을 걸어 작업중이던 쓰레드가 마칠때까지 다른 쓰레드에게 제어권이 넘어가지 않게 보호한다.

 - Synchronized 블럭이 끝나면 lock이 풀리고 다른 쓰레드도 접근 가능하게 된다.

 - 교착상태(dead-lock)이 빠질 위험이 있으므로 주의한다.

 

 

 

 

String Constant Pool

String 변수에 값을 할당하는 방법은 2가지가 있습니다.

  • Literal 변수를 대입하는 방법
  • new 키워드를 사용하는 방법

위 두가지 방식을 사용하여 예시코드를 작성해보겠습니다.

        String str1 = "hello";
        String str2 = new String("hello");
        String str3 = "hello";
        String str4 = new String("hello");

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

4개의 변수 모두 "hello"라는 문자열을 갖는데 3개의 주소비교(==)의 결과가 왜 다를까요?

그 이유는 String 타입 값 할당 방식에 따른 저장 방식이 다르기 때문입니다.

 

1. Literal 값으로 값을 할당하는 경우

String 을 Literal 값으로 할당하는 경우엔 Heap 메모리 영역안의 특별한 메모리 공간인 String constant pool 에 저장됩니다. String constant pool에 존재하는 Literal 값을 사용하게 된다면 새롭게 Literal 값을 만들어 String constant pool에 저장하는 것이 아닌, 현재 존재하는 값을 사용하게 됩니다.

위와 같은 이유로 System.out.println(str1==str3); 의 실행 결과가 true 가 나오게 되는 것입니다.

 

2. new 키워드로 값을 할당하는 경우

new 키워드를 통해 String 변수에 값을 할당하게 되면 일반적인 객체와 동일하게 Heap 영역에 동적으로 메모리 공간이 할당되게 됩니다.

마찬가지로 같은 문자열이더라도 new 키워드를 한번 더 사용하게 되면 같은 값이지만 다른 메모리 공간(Heap 영역 안)을 참조하게 됩니다.

위와 같은 이유로 System.out.println(str3==str4); 에서 false 를 출력하게 되는 것입니다.

그리고 System.out.println(str1==str2); 에서도 false 를 출력하는 것도 서로 상이한 메모리 공간을 참조하기 때문입니다.

  • strA → Heap → String Constant Pool
  • strB → Heap

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

https://ifuwanna.tistory.com/221

https://velog.io/@heoseungyeon/StringBuilder%EC%99%80-StringBuffer%EB%8A%94-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EB%8A%94%EA%B0%80

728x90
반응형
LIST