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
가 가장 빠름