9.2 String 클래스와 StringBuilder (String & StringBuilder)
자바 프로그래밍에서 가장 많이 다루는 데이터 타입 중 하나가 바로 텍스트 즉, 문자열(String) 입니다. java.lang 패키지에 포함된 String 클래스는 텍스트를 담아 제어하는 강력한 기능들을 무수히 제공합니다.
이번 장에서는 String 클래스의 핵심 작동 방식인 불변성(Immutability)과, 이를 보완해주는 StringBuilder, StringBuffer 클래스에 대해 알아봅니다.
1. String의 정체와 '불변성(Immutability)'
자바에서 자주 쓰이는 String은 단순한 자료형이 아니라 "문자열을 담고 있는 클래스(객체)"입니다. 객체이기 때문에 내부에 문자열과 관련된 엄청나게 많은 메서드를 내장하고 있습니다.
하지만 String 클래스를 쓸 때 반드시 기억해야 할 가장 결정적인 성질이 있습니다. 바로 한 번 생성된 String의 내용물은 메모리 상에서 절대로 변경할 수 없다(불변성) 는 점입니다.
String a = "Hello";
a = a + " World"; // "Hello"를 쪼개서 뒤에 " World"를 이어붙인 것일까요?
System.out.println(a); // Hello World
개발자의 눈에는 a라는 상자 안의 텍스트가 "Hello World"로 잘 "수정"된 것처럼 보이지만, 컴퓨터 메모리 내부 사정은 완전히 다릅니다.
- 최초에 메모리 어딘가에
"Hello"라는 텍스트 인스턴스가 덩그러니 생겨납니다. + " World"가 실행되면, 원래 있던"Hello"뒤에 글자를 가져다 붙이는 방식이 아니라, 메모리의 빈 구석에 아예 ** 새로운**"Hello World"문자열 인스턴스를 통째로 새로 만들어 냅니다!- 그리고
a가 가리키던 주소의 방향폰트를 방금 새로 만든"Hello World"로 바꿉니다. 옛날의"Hello"는 고아가 되어 우주 쓰레기로 떠돌다 버려집니다.
만약 게시판의 글을 조립하기 위해 String에 데이터 백만 개를 루프를 돌며 합친다면(concat), 메모리 상에는 백만 개의 새 인스턴스가 계속 만들어지고 버려지는 최악의 메모리 폭탄이 벌어집니다.
2. 유용한 String 메서드 대표 5가지
String 안에는 수십 개의 유용한 기능이 있습니다만, 현업에서 수도 없이 치는 주요 메서드를 살펴보겠습니다.
length(): 문자열의 길이를 반환합니다.("Java".length() -> 4)charAt(int index): 특정 위치(인덱스)의 문자 한 글자를 반환합니다.("Java".charAt(0) -> 'J')substring(int start, int end): 지정된 범위 사이의 문자열을 잘라냅니다.("Hello World".substring(0, 5) -> "Hello")replace(old, new): 지정된 문자를 새로운 문자로 모두 바꿉니다.split(String regex): 기준이 되는 문자열을 주면 텍스트를 칼로 썰듯 토막내어 배열로 반환합니다.("apple,banana,orange".split(",") -> ["apple", "banana", "orange"])
3. String의 단점을 메워주는 구원자: StringBuilder
위에서 String을 이용해 문자를 합칠 때마다 계속해서 새 인스턴스가 만들어지는 "불변성" 문제점을 지적했습니다. 이를 극복하려면 변경 가능한(Mutable) 문자열 클래스 인 StringBuilder(혹은 StringBuffer)를 사용해야 합니다.
StringBuilder는 내부에 넉넉한 문자열 공간(버퍼)을 가지고 있어서, 값을 합치거나 수정할 때 메모리에서 새 인스턴스를 무식하게 계속 만들지 않고, 자기 자신의 공간에 직접 글자를 밀어 넣습니다. 메모리와 속도 면에서 비교할 수 없을 만큼 압도적으로 빠릅니다.
public class StringBuilderExample {
public static void main(String[] args) {
// 내부에 문자열을 담을 넉넉한 공간(Buffer)을 생성합니다.
StringBuilder sb = new StringBuilder("Start!");
// 1. append() : 문자열을 맨 뒤에 가장 빠르게 추가합니다!
sb.append(" Java");
sb.append(" Programming");
// 2. insert() : 문자열 중간, 특정 인덱스에 글자를 끼워 넣습니다.
sb.insert(6, " Fun");
// 3. delete() : 특정 인덱스 범위의 문자들을 싹 삭제합니다.
sb.delete(0, 6);
// 최종적으로 온전한 String 타입으로 변환시켜 뽑아냅니다.
String result = sb.toString();
System.out.println(result);
}
}
[실행 결과]
Fun Java Programming
요약: 언제 무엇을 쓸까요?
String: 변경될 일이 없는 상수 문자열이나 일반적인 짧은 텍스트를 다룰 때 씁니다. (안전하게 데이터를 다룰 수 있어 쓰기 편합니다.)StringBuilder: 수없이 반복문 안에서 텍스트를 이어붙이거나 떼어내는 등 텍스트 조합/합성 로직이 돌 때 ** 필수적**으로 사용합니다.
(참고: StringBuffer는 멀티스레드 환경에서 안전한 버전이고, StringBuilder는 동기화 기능을 빼서 속도가 더 빠른 싱글스레드용 버전입니다. 대부분 StringBuilder를 더 선호합니다.)