63. 문자열 연결은 느리니 주의하라
문자열 연결 연산자(+)란?
자바에서 +
연산자를 사용하면 두 문자열을 쉽게 합칠 수 있다
문자열 연결은 자바에서 가장 기본적인 연산 중 하나이다
// 문자열 연결 예시
String firstName = "홍";
String lastName = "길동";
String fullName = firstName + lastName; // "홍길동"
문자열 연결 연산자(+)의 문제점
+
연산자는 편리하지만 많은 문자열을 연결할 때 심각한 성능 문제가 발생한다
✔️ 문자열 연결 연산자로 문자열 n개를 잇는 시간은 n²에 비례한다
왜 이런 일이 발생할까?
String은 불변(immutable)이다
한번 생성된 문자열은 변경할 수 없다
두 문자열을 연결하면 항상 새로운 String 객체가 생성된다
매번 새로운 복사본이 만들어진다
기존 내용을 복사하고 새 내용을 추가해야 한다
연결할수록 복사할 내용이 늘어난다
// 잘못된 예 - 문자열 연결 연산자를 반복문에서 사용
String result = "";
for (int i = 0; i < 100000; i++) {
result += "a"; // ❌ 매우 느림!
}
위 코드의 실행 과정
result += "a"
→ 새 문자열을 위한 메모리 할당result
의 내용을 새 메모리에 복사"a"를 복사한 내용 뒤에 추가
새 문자열 객체 생성
반복...
StringBuilder로 해결하기
자바는 문자열 연결 성능 문제를 해결하기 위해 StringBuilder
라는 클래스를 제공한다
StringBuilder의 특징
가변(mutable)이다
내용을 변경할 수 있다
새 객체를 만들지 않고 기존 객체에 내용을 추가한다
내부적으로 크기가 조정되는 배열을 사용한다
필요에 따라 자동으로 용량이 늘어난다
문자열 연결이 선형 시간(n)에 비례한다 (+ 연산자의 제곱 시간보다 훨씬 빠름)
// 좋은 예 - StringBuilder 사용
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append("a"); // ✅ 훨씬 빠르다!
}
String result = sb.toString();
StringBuilder의 주요 메소드
append()
: 문자열 끝에 내용 추가insert()
: 지정한 위치에 내용 삽입delete()
: 특정 범위의 내용 삭제toString()
: 완성된 문자열로 변환
성능 비교
문자열 연결 방식에 따른 성능 차이를 살펴보자
// 1. String 연결 연산자(+)
String str = "";
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str += "a";
}
long end = System.currentTimeMillis();
System.out.println("String +: " + (end - start) + "ms"); // 약 3000ms 이상
// 2. StringBuilder
StringBuilder sb = new StringBuilder();
start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
sb.append("a");
}
String sbResult = sb.toString();
end = System.currentTimeMillis();
System.out.println("StringBuilder: " + (end - start) + "ms"); // 약 5ms
StringBuffer vs StringBuilder
문자열 연결을 위한 클래스로 StringBuffer도 있다. 차이점은 무엇일까?
두 클래스의 차이점
StringBuffer: 스레드 안전(thread-safe)
여러 스레드가 동시에 접근해도 안전하다
synchronized
키워드로 동기화되어 있다멀티스레드 환경에서 사용해야 할 때 적합하다
StringBuilder: 스레드 안전하지 않음
동기화되어 있지 않아 더 빠르다
단일 스레드에서 사용할 때 적합하다
일반적인 상황에서는 StringBuilder를 사용하는 것이 좋다
// StringBuffer 예시 (멀티스레드 환경)
StringBuffer buffer = new StringBuffer();
buffer.append("안녕하세요");
buffer.append(" 반갑습니다");
// StringBuilder 예시 (일반적인 사용)
StringBuilder builder = new StringBuilder();
builder.append("안녕하세요");
builder.append(" 반갑습니다");
결론
한 번만 연결하거나 적은 양의 문자열을 연결할 때
+
연산자 사용해도 괜찮다
반복문에서 문자열을 연결하거나 많은 양의 문자열을 연결할 때
StringBuilder
를 사용하자!성능이 중요하다면 StringBuilder의 초기 용량을 지정하는 것도 좋다
// 초기 용량 지정 StringBuilder sb = new StringBuilder(1000);
멀티스레드 환경에서는
StringBuffer
를 고려하자
🧩 어려웠던 점
자바의 String이 불변이라는 개념과 이로 인해 +
연산자가 왜 성능 문제를 일으키는지 이해하는 데 시간이 걸렸다
StringBuilder와 StringBuffer의 차이점과 각각 언제 사용해야 하는지 구분하는 것이 처음에는 헷갈렸다
💡 느낀 점
작은 최적화가 큰 성능 차이를 만들 수 있다는 것을 깨달았다.
Last updated