STUDY NOTE · 개념 정리
String vs StringBuilder vs StringBuffer 정리: 불변 객체와 문자열 풀
Java
String은 불변(immutable),StringBuilder와StringBuffer는 가변(mutable)이다. 둘 중StringBuffer만 메서드가synchronized라 스레드 안전하다. 문자열을 자주 바꾸면 가변 쪽을, 그중 한 스레드에서만 쓰면StringBuilder를 쓴다.
1. 한눈에 비교
| 구분 | String | StringBuilder | StringBuffer |
| 변경 | 불가 (바꾸면 새 객체) | 가능 (내부 배열 수정) | 가능 (내부 배열 수정) |
| 스레드 안전 | 안전 (애초에 안 바뀜) | 안전하지 않음 | 안전 (synchronized) |
| 속도 | 이어 붙이기가 많으면 느림 | 가장 빠름 | 락 비용만큼 조금 느림 |
| 등장 | JDK 1.0 | JDK 1.5 | JDK 1.0 |
| 주 용도 | 값, 키, 상수 | 반복문에서 문자열 조립 | 여러 스레드가 같은 버퍼를 고칠 때 (드묾) |
2. String은 왜 불변인가
String s = "hello";
s.concat(" world"); // 새 문자열을 만들어 반환할 뿐, s는 그대로
System.out.println(s); // hello
s = s + " world"; // s가 '새 객체'를 가리키도록 바뀐 것
- 문자열 풀(String Pool)을 쓰기 위해: 같은 리터럴은 힙의 풀에서 한 객체를 같이 쓴다. 누가 바꿀 수 있으면 공유할 수 없다.
- 해시 캐싱:
hashCode()를 한 번 계산하면 저장해 두고 재사용한다. 그래서HashMap키로 쓰기 좋다. - 보안: 파일 경로, URL, DB 접속 정보 같은 값이 검사 뒤에 바뀌지 않는다.
- 스레드 안전: 바뀌지 않으니 동기화 없이 공유해도 된다.
3. 리터럴 vs new, == vs equals
String a = "java";
String b = "java";
String c = new String("java");
a == b; // true (풀의 같은 객체)
a == c; // false (c는 힙에 새로 만든 객체)
a.equals(c); // true (내용 비교)
a == c.intern(); // true (풀에 있는 객체를 돌려줌)
| 비교 | 의미 |
== | 같은 객체(주소)인가 |
equals() | 내용이 같은가 (String이 오버라이드) |
intern() | 풀에 같은 문자열이 있으면 그 객체, 없으면 풀에 넣고 반환 |
면접 단골: new String("java")는 객체를 몇 개 만드나? 풀에 "java"가 없었다면 리터럴 1개 + 힙 객체 1개로 최대 2개다.
4. + 연산과 StringBuilder
"a" + "b"처럼 컴파일 때 값이 정해진 상수끼리는 컴파일러가 "ab" 하나로 합친다. 변수가 끼면 실행 중에 이어 붙이는데, 반복문 안에서 +=를 쓰면 매번 새 String이 생긴다.
// 나쁨: 반복마다 새 문자열 생성 → 길이 n이면 대략 O(n²) 복사
String s = "";
for (int i = 0; i < 10000; i++) s += i;
// 좋음: 내부 배열 하나를 늘려 가며 수정 → 대략 O(n)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) sb.append(i);
String result = sb.toString();
- JDK 9부터 한 줄짜리
+연결은invokedynamic(StringConcatFactory)으로 최적화돼서, 반복문이 아닌 곳에선+를 써도 충분하다. - 크기를 미리 알면
new StringBuilder(용량)으로 배열 확장 횟수를 줄인다. 기본 용량은 16이고, 모자라면 대략 두 배로 늘린다.
5. 정리
- 바뀌지 않는 값 →
String - 한 스레드에서 문자열 조립 →
StringBuilder(대부분의 경우) - 여러 스레드가 같은 버퍼를 고침 →
StringBuffer(보통은 설계를 바꾸는 게 낫다) - 내용 비교는 항상
equals(),==는 같은 객체인지 볼 때만