"꾸준하고 완벽한 한 걸음"

Programming Language/Java

[Java] 문자열 + 연산과 StringBuilder 비교

kimyoungrok 2024. 11. 9. 02:04
728x90

 

Java에서 문자열 결합 시 많이 사용하는 + 연산은 직관적이고 간단하지만, 성능 측면에서 적절하지 않은 경우가 있습니다. 이번 글에서는 문자열 결합의 효율적인 방법과 이유에 대해 설명하겠습니다.

Java에서의 + 연산

Java의 String은 immutable(불변)한 객체입니다. 따라서 String 간의 + 연산은 기존 객체를 수정하지 않고, 새로운 String 객체를 생성하게 됩니다. 이는 다음과 같은 코드에서 성능 문제가 발생할 수 있습니다.

// 비효율적인 코드 예시
String res = "";
for (int i = 0; i < 100; ++i) {
    res += i; // 매번 새로운 String 객체 생성
}

 

위 코드는 for 문이 실행될 때마다 새로운 String 객체를 생성하여 성능 저하를 유발합니다.

 

 

+ 연산이 효율적인 경우

단일 라인의 +연산에서는 Java 컴파일러가 내부적으로 최적화합니다.

String res = "Hello" + " " + "World" + "!";

컴파일러가 위 코드를 다음과 같이 변환할 수 있습니다.

String res = new StringBuilder()
                    .append("Hello")
                    .append(" ")
                    .append("World")
                    .append("!")
                    .toString();

즉, 단일 라인의 경우에는 + 연산도 최적화되어 StringBuilder를 사용하는 것과 동일한 효과를 얻을 수 있습니다.

이는 JLS 15.18.1에 명시되어 있습니다.

https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.18.1

 

Chapter 15. Expressions

 

docs.oracle.com

To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.

 

 

반복문에서는 왜 + 연산이 비효율적인가?

단일 라인의 + 연산은 최적화가 되지만, 반복문에서는 결국 매번 새로운 문자열 객체를 생성합니다.

따라서, 문자열을 여러 번 결합하는 경우 mutable한 문자열 결합 클래스를 사용하는 것이 좋습니다.

 

StringBuilder

StringBuilder는 수정 가능한 가변 문자열 클래스입니다.

객체를 한 번 생성한 후 append() 메서드를 사용해 문자열을 효율적으로 결합할 수 있습니다.

append()는 다양한 객체에 대한 명시적 형 변환을 지원합니다. 단, 내부적으로는 객체의 toString()을 호출해 문자열을 변환합니다.

public class Main {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < 10; ++i) {
            sb.append(i).append('\n');
        }
        System.out.print(sb); // 연결한 문자열이 출력된다.
    }
}

 

멀티스레드 환경에서는 StringBuffer

StringBuilder는 Non-Synchronized 클래스입니다. 멀티스레드 환경에서 동기화 문제가 발생할 수 있기 때문에, 이 경우에는 StringBuffer를 사용해 안전하게 문자열 결합을 수행할 수 있습니다.

 

StringJoiner

StringJoiner는 구분자(Delimiter) 기반 문자열 결합 클래스입니다.

구분자를 자동으로 추가해주기 때문에 StringBuilder보다 코드가 더 간결하고 명확해집니다.

add() 는 CharSequence 만을 인자로 받는다.

public class Main {
    public static void main(String[] args) {
        StringJoiner sj = new StringJoiner("\n");
        for (int i = 1; i < 10; ++i) {
            sj.add(String.valueof(i)); // 구분자를 추가하지 않아도 된다.
        }
        System.out.print(sj);
    }
}

다음과 같이 구분자 외에도 접미사/접두사를 지정할 수 있습니다.

public class Main {
    public static void main(String[] args) {
        StringJoiner joiner = new StringJoiner(", ", "[", "]");
        joiner.add("Apple").add("Banana").add("Orange");
        System.out.println(joiner.toString()); // 출력: [Apple, Banana, Orange]
    }
}

 

결론

+ 연산은 단일 라인에서는 효율적이지만, 반복문 등 복잡한 상황에서는 성능 저하의 원인이 됩니다. 이러한 경우에는 StringBuilder나 StringJoiner와 같은 클래스를 사용해 성능을 개선할 수 있습니다.

728x90