목차
String, StringBuffer, StringBuilder 비교
Java에서 문자열을 다룰 때, String, StringBuffer, StringBuilder 세 가지 주요 클래스를 사용할 수 있습니다.
자바 기본 상식 중의 하나로 꼭 숙지하시길 바랍니다.
각각의 특징과 차이점을 살펴보겠습니다.
1. String (불변 객체, Immutable)
String은 불변(Immutable) 객체입니다. 즉, 한 번 생성된 문자열은 변경할 수 없습니다.
변경하려고 하면, 기존 문자열이 수정되는 것이 아니라 새로운 객체가 생성됩니다.
📌 특징
- 불변(Immutable) → 문자열이 변경될 때 새로운 객체가 생성됨
- 리터럴(literal) 사용 가능 → "Hello"처럼 직접 할당 가능
- String Pool 사용 → 동일한 문자열을 재사용하여 메모리 절약 가능
( Java에서는 String Pool(문자열 풀) 이라는 메모리 영역을 사용하여 동일한 문자열을 공유합니다.
즉, 동일한 문자열 리터럴이 있으면 기존 객체를 재사용하여 메모리를 절약합니다.) - 성능 저하 가능성 → 문자열을 자주 수정하는 경우 새로운 객체가 반복적으로 생성되어 성능 저하 발생
📌 예제
public class StringExample {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = str1 + " World"; // 새로운 객체 생성
System.out.println(str1); // "Hello"
System.out.println(str2); // "Hello World"
String str = "Hello";
str = str + " World2";
System.out.println(str); // "Hello World2"
}
}
- 위 코드에서 str2 = str1 + " World"; 를 수행하면 기존 str1을 수정하는 것이 아니라, 새로운 String 객체가 생성됩니다
- 두번째에서 "Hello World2"는 새로운 String 객체가 생성된 것입니다.
이전의 "Hello" 객체는 더 이상 사용되지 않아 Garbage Collector (GC) 에 의해 제거됩니다.
2. StringBuffer (가변 객체, Mutable, 스레드 안전)
StringBuffer는 가변(Mutable) 객체로, 문자열을 변경할 때 새로운 객체를 생성하지 않고 기존 객체를 수정합니다. 또한 스레드 안전(Thread-safe) 하여 멀티스레드 환경에서도 안전하게 사용할 수 있습니다.
📌 특징
- 가변(Mutable) → 문자열을 변경해도 새로운 객체가 생성되지 않음
- 성능 개선 → String보다 문자열 조작이 빈번할 때 유리
- 스레드 안전(Thread-safe) → synchronized 키워드 사용으로 멀티스레드 환경에서 안전
- 비교적 속도가 느림 → synchronized로 인해 동기화 비용이 발생 ( StringBuilder보다 느림.)
📌 예제
public class StringBufferExample {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // 기존 객체 변경
System.out.println(sb); // "Hello World"
}
}
- append()는 기존 문자열을 변경하므로 새로운 객체를 만들지 않습니다.
- 메모리 사용량이 줄어들고 성능이 String보다 향상됩니다.
public class StringBufferThreadExample {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello");
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
sb.append("!");
System.out.println(sb);
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
- StringBuffer는 동기화(synchronized)가 적용되어 멀티스레드 환경에서도 안전하게 동작합니다.
3. StringBuilder (가변 객체, Mutable, 스레드 비안전)
StringBuilder는 StringBuffer와 동일한 가변(Mutable) 객체이지만, 스레드 안전(Thread-safe)이 보장되지 않습니다. 대신 동기화(synchronized)를 하지 않기 때문에 실행 속도가 더 빠릅니다.
📌 특징
- 가변(Mutable) → 문자열을 변경해도 새로운 객체가 생성되지 않음
- 성능이 가장 빠름 → StringBuffer보다 동기화 비용이 없어 더 빠름
- 스레드 안전하지 않음 → 멀티스레드 환경에서는 직접 동기화 필요
- 싱글스레드 환경에서 유리
📌 예제
public class StringBuilderExample {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb); // "Hello World"
}
}
- 멀티스레드 환경이 아니라면 StringBuffer 대신 StringBuilder를 사용하는 것이 더 좋습니다.
- StringBuffer와 사용법이 동일하지만, synchronized가 없어서 더 빠름
4. String vs StringBuffer vs StringBuilder 비교
특성 | String | StringBuffer | StringBuilder |
가변성 | ❌ (불변, Immutable) | ✅ (가변, Mutable) | ✅ (가변, Mutable) |
메모리 관리 | 새 객체 생성 | 기존 객체 수정 | 기존 객체 수정 |
성능 | 느림 (새 객체 생성) | 중간 (동기화로 인해 약간 느림) | 가장 빠름 |
스레드 안전성 | 안전 (불변 객체라 동기화 불필요) |
안전 (synchronized 사용) |
안전하지 않음 |
멀티스레드 환경 추천 여부 |
O | O | X |
5. 언제 사용해야 할까?
- String: 문자열이 자주 변경되지 않고, 불변성을 유지해야 하는 경우 (String Pool을 활용하여 메모리 절약 가능)
- StringBuffer: 멀티스레드 환경에서 문자열을 자주 변경해야 하는 경우
- StringBuilder: 단일 스레드 환경에서 문자열을 자주 변경해야 하는 경우
6. 성능 비교 (테스트 코드)
아래 코드를 실행하면 String이 가장 느리고, StringBuilder가 가장 빠르다는 것을 확인할 수 있습니다.
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 += "a";
}
endTime = System.nanoTime();
System.out.println("String 수행 시간: " + (endTime - startTime) + " ns");
// StringBuffer 테스트
startTime = System.nanoTime();
StringBuffer sbf = new StringBuffer();
for (int i = 0; i < 10000; i++) {
sbf.append("a");
}
endTime = System.nanoTime();
System.out.println("StringBuffer 수행 시간: " + (endTime - startTime) + " ns");
// StringBuilder 테스트
startTime = System.nanoTime();
StringBuilder sbd = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sbd.append("a");
}
endTime = System.nanoTime();
System.out.println("StringBuilder 수행 시간: " + (endTime - startTime) + " ns");
}
}
결과 예시 (실제 실행 환경에 따라 다를 수 있음)
String 수행 시간: 123456789 ns
StringBuffer 수행 시간: 56789 ns
StringBuilder 수행 시간: 34567 ns
- String이 가장 느리고, StringBuffer는 synchronized로 인해 StringBuilder보다 조금 느림
✅ 결론
- 문자열 변경이 적다면 String을 사용 (불변 객체로 메모리 절약 가능)
- 멀티스레드 환경에서 문자열 변경이 많다면 StringBuffer 사용 (스레드 안전)
- 싱글스레드 환경에서 문자열 변경이 많다면 StringBuilder 사용 (가장 빠름)
이제 String, StringBuffer, StringBuilder의 차이점을 이해하고 상황에 맞게 선택해서 성능을 최적화하세요! 🚀
'이직&취업 > Java 기초 상식' 카테고리의 다른 글
JAVA thread safe 란? (12) | 2025.03.21 |
---|---|
브라우저에 www.naver.com을 입력하면??? (2) | 2025.03.19 |
Java Generic(제네릭) 란? (16) | 2025.03.18 |
JAVA 버전 별 특징 및 주요 추가 기능 (5) | 2025.03.17 |
JVM(Java Virtual Machine)과 자바 가비지 컬렉션(GC) (4) | 2025.03.17 |