Java에서 문자열을 다룰 때 가장 많이 사용하는 클래스는 String, StringBuilder, StringBuffer입니다. 하지만 각각의 특성과 사용 용도가 다르기 때문에 올바르게 선택하는 것이 중요합니다. 이번 글에서는 이 세 가지 클래스의 차이점과 어떤 상황에서 어떤 클래스를 사용해야 하는지 정리해 보겠습니다.
String (불변 객체, Immutable)String 클래스는 불변(Immutable) 한 객체입니다. 즉, 문자열을 변경하면 새로운 객체가 생성되며, 기존 객체는 변경되지 않습니다.
String Pool 사용: 동일한 문자열이 여러 개 존재할 경우 String Pool을 사용하여 중복을 방지.String str1 = "Hello";
String str2 = str1 + " World"; // 새로운 String 객체가 생성됨
System.out.println(str1); // Hello
System.out.println(str2); // Hello World
StringBuilder (가변 객체, Mutable, 비동기)StringBuilder는 가변(Mutable) 객체로, 문자열을 수정할 때 새로운 객체를 생성하지 않고 기존 객체의 내부 배열을 변경합니다.
String보다 문자열 변경이 많은 경우 성능이 뛰어남.StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 기존 객체에 문자열을 추가
System.out.println(sb); // Hello World
StringBuffer (가변 객체, Mutable, 동기)StringBuffer는 StringBuilder와 동일하지만 스레드 안전(Thread-safe) 합니다. 내부적으로 synchronized 키워드를 사용하여 멀티 스레드 환경에서도 안전하게 동작합니다.
synchronized 사용으로 인해 StringBuilder보다 성능이 낮음.StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // 기존 객체에 문자열을 추가
System.out.println(sb); // Hello World
String, StringBuilder, StringBuffer 비교 정리String | StringBuilder | StringBuffer | |
|---|---|---|---|
| 가변성 | 불변 (Immutable) | 가변 (Mutable) | 가변 (Mutable) |
| 성능 | 느림 (새로운 객체 생성) | 빠름 (객체 재사용) | 상대적으로 느림 (synchronized 사용) |
| 스레드 안전성 | O (String 자체가 불변이라 안전) | X (스레드 안전하지 않음) | O (synchronized 사용) |
| 사용 추천 환경 | 변경이 거의 없는 문자열 | 단일 스레드에서 문자열 변경이 많은 경우 | 멀티 스레드에서 문자열 변경이 많은 경우 |
String, StringBuilder, StringBuffer 성능 비교 테스트다음은 세 가지 클래스를 활용한 성능 테스트 코드입니다.
public class StringPerformanceTest {
public static void main(String[] args) {
long startTime, endTime;
// String 테스트
startTime = System.nanoTime();
String str = "";
for (int i = 0; i < 10000; i++) {
str += i; // 매번 새로운 객체가 생성됨
}
endTime = System.nanoTime();
System.out.println("String 실행 시간: " + (endTime - startTime) / 1_000_000 + "ms");
// StringBuffer 테스트
startTime = System.nanoTime();
StringBuffer sbf = new StringBuffer();
for (int i = 0; i < 10000; i++) {
sbf.append(i); // 기존 객체에 추가됨
}
endTime = System.nanoTime();
System.out.println("StringBuffer 실행 시간: " + (endTime - startTime) / 1_000_000 + "ms");
// StringBuilder 테스트
startTime = System.nanoTime();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // 기존 객체에 추가됨
}
endTime = System.nanoTime();
System.out.println("StringBuilder 실행 시간: " + (endTime - startTime) / 1_000_000 + "ms");
}
}
String 실행 시간: 76ms
StringBuilder 실행 시간: 0ms
StringBuffer 실행 시간: 1ms
String은 객체가 계속 생성되므로 매우 느림StringBuffer는 synchronized로 인해 약간 느림StringBuilder가 가장 빠름