DEV ℧ Developer Diary

[EffectiveJava] item63 - 문자열 연결은 느리니 주의하라

문자열 연결 연산자 (+)는 여러 문자열을 하나로 합쳐주는 편리한 수단이다.

한 줄짜리 출력값 혹은 작고 크기가 고정된 객체의 문자열 표현을 만들 때라면 괜찮지만, 본격적으로 사용하기 시작하면 성능 저하를 감내하기 어렵다.

문자열 연결 연산자로 문자열 n개를 잇는 시간은 n²에 비례한다. 문자열은 불변이라서 두 문자열을 연결할 경우 양쪽의 내용을 모두 복사해야 하므로 성능저하는 피할 수 없는 결과다.

연결 연산자(+)

문자열 연결을 잘못 사용한 예 - 느리다.

public String statement() {
    String result = "";
    for (int i = 0; i < numItems(); i++)
        result += lineForItem(i); /** 문자열 연결 */
    return result;
}

품목이 많을 경우 이 메서드는 심각하게 느려질 수 있다.
성능을 포기하고 싶지 않다면 String 대신 StringBuilder를 사용하자.

StringBuilder를 사용하면 문자열 연결 성능이 크게 개선된다.

public String statement2() {
    StringBuilder b = new StringBuilder(numItems() + LINE_WIDTH);
    for (int i = 0; i < numItems(); i++)
        b.append(lineForItem(i));
    return b.toString();
}

자바 6 이후 문자열 연결 성능을 다방면으로 개선했지만, 이 두 메서드의 성능 차이는 여전히 크다. 품목을 100개로 하고 lineForItem이 길이 80인 문자열을 반환하게 하여 컴퓨터에서 실행했을때 String보다 StringBuffer가 6.5배나 빨랐다.

statement 메서드의 수행 시간은 품목 수의 제곱이 비례해 늘어나고 statement2는 선형으로 늘어나므로, 품목수가 늘어날수록 성능 격차도 점점 벌어질 것이다.

실제로 랜덤으로 80자리의 문자열을 10만건 정도 생성하여 각각 String, StringBuffer로 작업을 해보았다. 결과는 아래와 같았다.

len: 80, size: 100000
String 작업 시간: 89952 m/s
StringBuffer 작업 시간: 15 m/s

생성하는 문자열을 증가 시킬 수록 많이 체감 될 정도로 속도 차이가 나는 것을 확인 할 수 있었다.