[Java] String, StringBuilder, StringBuffer : 상황에 맞게 문자열 다루기

Julie·2021년 8월 9일
0

Java

목록 보기
1/1

Java 언어는 문자열을 다루기 위한 클래스들을 제공하고 있습니다. 대표적인 클래스는 String, StringBuilder, StringBuffer 입니다. 세가지 클래스는 모두 문자열을 다루는 공통점을 가지고 있습니다. 하지만 각 클래스는 서로 다른 특성을 가지고 있습니다.

String, StringBuilder, StringBuffer 클래스는 어떻게 다를까요? 본 글에서 각 클래스의 특성과 차이점을 알아보겠습니다.

1. String


String은 문자열을 나타내는 클래스입니다.

String 클래스는 연속된 문자(문자열)을 나타내는 클래스입니다. String 객체는 두가지 방법으로 생성할 수 있습니다.

(1) 문자열 리터럴(쌍따옴표로 표현된 문자열)을 이용한 String 객체 생성

public static void main(String[] args) {
          String strLiteral1 = "Hello";
          String strLiteral2 = "World";
  }

(2) new 키워드를 이용한 String 객체 생성

public static void main(String[] args) {
	String strUsingNew1 = new String("Hello");
        String strUsingNew2 = new String("World");
}

잠깐! JVM은 문자열 상수 풀을 이용하여 문자열 리터럴을 관리합니다.

자바 힙 영역에는 문자열 상수 풀(String Constant Pool)이라는 문자열 리터럴을 저장하는 저장 공간이 있습니다. JVM(Java Virtual Machine)은 이 공간을 이용하여 메모리 공간을 효율적으로 관리합니다. 우리가 문자열 리터럴을 이용하여 String 객체를 생성할 때 JVM은 다음과 같이 동작합니다.

  1. 문자열 상수 풀에 동일한 문자열("hello")이 이미 존재하는지 확인한다.
  2. 문자열 상수 풀에 문자열이 있다면, 해당 문자열에 대한 참조를 반환한다.
  3. 문자열 상수 풀에 문자열이 없다면, 풀에 새로운 문자열을 생성하고 문자열에 대한 참조 값을 반환한다.

아래의 코드와 같이 문자열 리터럴을 이용하여 String 객체를 생성한다면 메모리 공간은 그림(fig1)과 같이 표현할 수 있습니다.

public static void main(String[] args) {
    String str = "hello";
}
fig1. 문자열 리터럴을 이용한 String 객체 생성 시 메모리 공간

여기서 주의할 점이 있습니다. new 키워드를 이용하여 String 객체를 생성할 때에는 문자열이 문자열 상수 풀에 저장되지 않습니다. new 키워드를 사용할 때 JVM은 힙 영역에 새로운 String 객체를 생성합니다. 따라서 메모리 공간 효율(재사용성)을 고려한다면 문자열을 생성할 때 new 키워드보다 문자열 리터럴을 사용하는 것이 더 좋습니다.

아래는new 키워드를 사용하여 String 객체를 생성하는 코드와 이를 실행했을 때의 메모리 공간 입니다.

public static void main(String[] args) {
    String strObj = new String("Hello");
}
fig2. new 키워드를 이용한 String 객체 생성 시 메모리 공간

String 객체는 변경할 수 없습니다.


String 객체는 한 번 생성되면 그 객체가 나타내는 문자열 값을 바꿀 수 없습니다. 이러한 특성을 "Immutable 하다"라고 표현합니다. 한 번 생성된 String 객체는 변경할 수 없기 때문에 JVM은 상수 풀에 저장된 문자열을 재사용할 수 있습니다. 만약 변경할 수 있다면, 언젠가 수정될 가능성이 있기 때문에 재사용할 수 없습니다. JVM은 이러한 String 객체의 특성을 이용하여 메모리 공간을 효율적으로 관리했던 것입니다.

그런데 종종 + 연산을 이용하여 String 객체가 가리키는 문자열을 수정한 경험이 있지 않나요? 사실은 문자열을 수정할 때마다 문자열 상수 풀에 새로운 문자열이 계속 생성되는 것이었습니다. 이러한 코드는 메모리 공간을 비효율적으로 사용하는 코드입니다!!

public static void main(String[] args) {
        String str = "Hello";
        str += " World";
        str += " !!!!";
}
fig3. + 연산을 이용한 문자열 수정 시 메모리 공간

2. StringBuilder


StringBuilder는 변경 가능한 문자열을 나타내는 클래스입니다.

StringBuilder는 변경할 수 있는(mutable) 문자열을 나타냅니다. StringBuilder는 문자열 값을 변경할 수 있는 append(), insert() 메소드를 제공합니다. StringBuilder를 사용한다면 새로운 객체를 생성지 않고 기존의 StringBuilder 객체를 이용하여 문자열을 수정할 수 있습니다. 문자열 변경이 잦다면, String 보다 StringBuilder를 사용하는 것이 좋습니다.

public static void main(String[] args) {
        StringBuilder stringBuilder = new StringBuilder("Hello");
        stringBuilder.append("World");
        stringBuilder.append("!!!!!");
}

StringBuilder는 멀티스레드 환경에서 동기화되지 않습니다.

StringBuilder 클래스는 본래 싱글스레드 환경에서 사용되는 목적으로 설계되었습니다. 따라서 StringBuilder 클래스가 제공하는 메소드는 동기화되지 않습니다.

동기화 여부는 StringBuilder와 StringBuffer의 가장 큰 차이점입니다. 아래에 언급할 StringBuffer 클래스도 변경할 수 있는 문자열을 나타내는 클래스입니다. StringBuffer 클래스는 StringBuilder와 동일한 형태의 메소드를 제공합니다. 하지만 멀티스레드 환경에서 동기화됩니다. 쉽게 말해 StringBuffer는 StringBuilder에 동기화 코드가 추가된 클래스입니다.

그렇다면 StringBuffer가 동기화까지 되니 더 좋겠네요? 상황에 따라 다릅니다. 싱글스레드 환경에서만 사용한다면, StringBuilder를 사용하는 것이 좋습니다. StringBuilder 객체는 동기화 관련 작업을 수행하지 않기 때문에 더 적은 코드로 문자열을 수정할 수 있습니다. 즉, 더 빠르게 동작합니다. 반면, 멀티스레드 환경에서 사용한다면, StringBuffer를 사용하는 것이 적절합니다.

3. StringBuffer


StringBuffer는 변경 가능한 문자열을 나타내는 클래스입니다.

StringBuffer 클래스는 StringBuilder와 동일하게 변경할 수 있는(mutable) 문자열을 나타내는 클래스입니다. 문자열을 변경하는 메소드 또한 StringBuilder와 동일한 형태로 제공됩니다.

public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer("Hello");
        stringBuffer.append("World");
        stringBuffer.append("!!!!!");
}

StringBuffer는 멀티스레드 환경에서 동기화됩니다.

StringBuffer 클래스의 메소드는 멀티스레드 환경에서 동기화됩니다. 그렇기 때문에 멀티스레드 환경에서는 StringBuilder 보다 StringBuffer를 사용하는 것이 적합합니다.

4. String, StringBuilder, StringBuffer 비교 분석


이렇게 다릅니다.

  • String 객체는 변경 가능하나 StringBuilder, StringBuffer 객체는 변경 불가능
  • StringBuilder는 멀티스레드 환경에서 동기화되지 않으나 StringBuffer는 동기화 됨

이렇게 사용하면 좋습니다.

  • 문자열의 변경이 잦은 환경에서는 StringBuilder, StringBuffer를 사용할 것
  • 멀티스레드 환경을 고려한다면 StringBuffer를 사용할 것
  • 싱글스레드 환경에서 StringBuilder를 사용할 것

5. 결론


String, StringBuilder, StringBuffer 클래스는 비슷하지만 각각 다른 특성을 가지고 있습니다. 우리는 각 클래스의 차이점을 이해하고, 문자열을 사용하는 상황에 따라 적절한 클래스를 사용하여 문자열을 다뤄야 합니다.

6. 참고 자료


profile
Software Engineer

0개의 댓글