반응형

오늘은 Java의 String에 대한 마지막 이야기! String VS StringBuilder 에 대해 살펴보겠습니다.

 

아직 1편과 2편을 보지 않으신 분들은 이전 포스팅에서 읽고 오시기를 권장합니다.

 

2019/12/19 - [JAVA] - Java의 String 이야기(1) - String은 왜 불변(Immutable)일까?

 

Java의 String 이야기(1) - String은 왜 불변(Immutable)일까?

제목에 적혀있다시피 Java에서 String은 불변(Immutable)입니다. 그럼 불변이란 무엇일까요? 불변은 말 그대로 아닐 불(不) 변할 변(變). 변하지 않는 것을 의미합니다. 그럼 Java에서 String이 불변이라는 말에..

readystory.tistory.com

2019/12/20 - [JAVA] - Java의 String 이야기(2) - String pool이란 무엇인가?

 

Java의 String 이야기(2) - String pool이란 무엇인가?

우리는 지난 포스팅을 통해 Java의 String은 왜 불변인가에 대해 알아보고, Java에서는 String을 String constant pool에서 관리한다는 것을 알아보았습니다. 2019/12/19 - [JAVA] - Java의 String 이야기(1) - St..

readystory.tistory.com

 

String과 덧셈(+) 연산자

 

String str = "Kim";	// str : "Kim"
str += "Ready"; // str : "KimReady"

위 코드는 str이라는 String 객체에 "Kim"이라는 문자열을 할당했다가 추가로 "Ready" 문자열을 이어주는 코드입니다.

Java의 String 이야기(1) 에서 살펴본 것처럼 Java에서 String이 불변(Immutable)이라는 것을 알고 있습니다.

따라서 위 코드에 대한 메모리 변화는 같습니다.

 

보시다시피 str이 처음에 가리키던 "Kim" 메모리에 "Ready" 문자열을 추가하는 것이 아니라, "Kim"에 "Ready"가 합쳐진 문자열로 아예 새로운 메모리를 다시 할당하게 됩니다. 이는 String이 불변이기 때문인데요. 이렇듯 문자열에 수정이 발생할 때마다 새로운 메모리를 할당받기 때문에 많은 양의 문자열 변경이 일어나야 하는 소요가 발생할 경우에 성능 저하를 불러일으킬 수 있습니다.

 

StringBuilder

하지만 StringBuilder를 사용한다면 얘기가 달라집니다.

Oracle Javadoc에서는 StringBuilder에 대해 다음과 같이 정의하고 있습니다.

A mutable sequence of characters.

우리가 눈여겨볼 것은 StringBuilder는 가변적인(mutable) 시퀀스라는 것입니다. StringBuilder는 문자열이 변경될 때마다 새로운 메모리를 할당받는 것이 아니라, 버퍼를 통해 문자열을 관리하고 있다가 toString()을 통해 String 객체를 생성하게 됩니다.

 

StringBuilder는 기존 버퍼에 있던 문자열 끝에 추가하는 append() 메소드와 원하는 위치에 삽입할 수 있는 insert() 메소드를 제공합니다. 각각의 메소드에 대해 예제 코드를 살펴보겠습니다.

StringBuilder sb = new StringBuilder("abc");  // "abc"
sb.append("ghi");  // "abcghi"
sb.insert(3, "def");  // "abcdefghi"
System.out.println(sb.toString()); // "abcdefghi"

StringBuilder의 생성자는 비어있을 수도, 문자열을 넣어줄 수도, 초기 용량(capacity)을 설정해줄 수도 있습니다.

 

그렇다면 이제 본격적으로 String과 StringBuilder의 비교를 해보도록 하겠습니다.

 

String VS StringBuilder

유의미한 성능차이를 확인하기 위해서 반복문을 통해 10만번의 문자열 추가 연산을 실행해보도록 하겠습니다.

public class Main {
    private static final int MAX_COMPARE = 100000;

    public static void main(String[] args) {
        // String Test
        String test1 = "";
        long start = System.currentTimeMillis();
        for(int i = 0; i < MAX_COMPARE; i++) {
            test1 += "a";
        }
        long end = System.currentTimeMillis();
        System.out.println("String : " + (end - start) + " ms");

        // StringBuilder Test
        StringBuilder sb = new StringBuilder();
        start = System.currentTimeMillis();
        for(int i = 0; i < MAX_COMPARE; i++) {
            sb.append("a");
        }
        end = System.currentTimeMillis();
        System.out.println("StringBuilder : " + (end - start) + " ms");
    }
}

보시다시피 엄청나게 차이가 납니다.

따라서 반복문을 통해 많은 양의 문자열 변경이 일어나야 하는 경우에는 StringBuilder를 사용하시는 것이 더 좋습니다.

 

그럼 StringBuilder는 어떤 경우에도 좋을까요?

대답은 No! 아닙니다. StringBuilder의 경우 멀티 쓰레드 환경에서 thread-safe 하지 않기 때문에 다수의 쓰레드에서 문자열을 변경해야 하는 경우에는 동기화 문제가 발생할 수 있습니다.

 

그래서 그럴 경우에는 StringBuilder가 아니라 StringBuffer를 사용하셔야 하는데요. StringBuffer는 StringBuilder와 동일한 API를 제공하면서 각각의 메소드에 대해 synchronization을 보장하기 때문에 멀티 쓰레드 환경에서 보다 안전하게 사용할 수 있습니다. 단, StringBuilder와 비교했을 때는 다소 느릴 수 있습니다.(그래도 String 보다는 훨씬 빠릅니다.)

 


P.S. 자바 8버전 이후부터 String 의 + 연산자를 통한 Concatenation에 대해 자바 컴파일러가 자동으로 StringBuilder로 변환해주고 있습니다. 따라서 자바 8 이상을 사용하시는 분이라면 일반적인 상황에서 StringBuilder를 고려하지 않으셔도 됩니다만, 반복문 내에서 이루어지는 Concatenation에 대해서는 StringBuilder로 변환해주지 않기 때문에 명시적으로 StringBuilder를 사용하시는 것이 좋습니다.

반응형
반응형