String-StringBuffer-StringBuilder

살펴보고 돌아서서 5분 후면 까먹는 String, StringBuffer, StringBuilder에 대한 정리.


String

StringImmutable Class (불변 클래스)이다. 불변 클래스라 함은 아래 코드와 같이 str1에 str2를 더했을 때 원래의 객체가 변하는 것이 아니라 새로운 객체가 생성되는 것을 의미한다.

코드 1.

1
2
3
4
5
6
7
8
String str1 = "Lazy";
String str2 = "Rodi";
System.out.println(" str1 => " + str1 + ": " + str1.hashCode());
System.out.println(" str2 => " + str2 + ": " + str2.hashCode());
str1 += str2;
System.out.println("str1 += str2; => " + str1 + ": " + str1.hashCode());

결과값 1. 주소가 바뀐 것을 확인할 수 있다.

1
2
3
str1 => Lazy: 2361236
str2 => Rodriguez: 2552738
str1 += str2; => LazyRodriguez: -1189801674

위의 예제에서 처음 str1이 차지하고 있던 공간이 낭비된다는 것을 알 수 있다.

이 낭비를 막기 위해 StringBuffer를 살펴보자.


StringBuffer

StringBufferImmutable Class가 아니며, append() 메소드를 제공하여 문자열 연산을 할 수 있게 도와준다.

코드 2.

1
2
3
4
5
6
7
8
9
StringBuffer strBuf = new StringBuffer();
String str1 = "Lazy";
String str2 = "Rodriguez";
System.out.println("strBuf => " + strBuf + " : " + strBuf.hashCode());
strBuf.append(str1);
System.out.println("strBuf => " + strBuf + ": " + strBuf.hashCode());
strBuf.append(str2);
System.out.println("strBuf => " + strBuf + ": " + strBuf.hashCode());

결과값 2. 새로 생성되는 객체가 없다.

1
2
3
strBuf => : 366712642
strBuf => Lazy: 366712642
strBuf => LazyRodriguez: 366712642

별 차이 없는 것으로 볼 수도 있고, StringBuffer를 생성할 때의 자원이 걱정될 수도 있겠지만, 문자의 + 연산이 빈번하게 이루어진다면 메모리의 낭비가 커질 수 밖에 없고 이 때 StringBufferString보다 효율적으로 사용할 수 있게 된다.

StringStringBuffer 모두 char[] 이다. String+ 연산 시 새로운 배열을 만들어내고 StringBuffer은 미리 배열을 여유있게 잡아둔다.

이런 이유때문에 StringBuffer의 배열이 꽉 차서 메모리를 더 잡아주는 지점에서 성능 저하가 살짝 나타난다.

보통 StringBufferString 보다 성능이 뛰어나다. String의 경우에도 Compiler가 + 연산을 StringBuffer로 자동 변환해주긴 하지만 모든 + 연산을 커버하지는 않는다고 한다.


StringBuilder

위에서 살펴본 StringBuffer는 멀티 스레딩 환경에서 Thread-safe를 위해 동기화(Synchronized)가 이루어진다.

StringBuilderStringBuffer에서 동기화 기능을 제거한 것이다. 고로, 멀티 스레딩 환경이 아니라면 StrigBuilder를 쓰는 것이 낫다.


성능시험

간단한 코드로 속도를 비교해 보도록 하자. loop 단위가 작을 때에는 currentTimeMillis() 대신 nanoTime()을 사용하면 된다.

(사실 아래 코드가 측정에 적당한 코드인지 불안하긴 하다…)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
String str1 = "Lazy";
String str2 = "Rodriguez";
StringBuffer strBuffer = new StringBuffer();
StringBuilder strBuilder = new StringBuilder();
int i = 0;
long max = 50000;
long s, e;
s = System.currentTimeMillis();
System.out.println("'StringBuffer' start = " + s);
for (i = 0; i < max; i++) {
strBuffer.append(str2);
}
e = System.currentTimeMillis();
System.out.println("'StringBuffer' start = " + e);
System.out.println("'StringBuffer' spend time = " + (e - s));
s = System.currentTimeMillis();
System.out.println("'StringBuilder' start = " + s);
for (i = 0; i < max; i++) {
strBuilder.append(str2);
}
e = System.currentTimeMillis();
System.out.println("'StringBuilder' start = " + e);
System.out.println("'StringBuilder' spend time = " + (e - s));
s = System.currentTimeMillis();
System.out.println("'+' start = " + s);
for (i = 0; i < max; i++) {
str1 += str2;
}
e = System.currentTimeMillis();
System.out.println("'+' start = " + e);
System.out.println("'+' spend time = " + (e - s));

아래 결과는 3회 측정 후 평균 낸 값이다.

기초적인 환경에서는 StringBuilder 성능이 제일 좋다는 것을 알 수 있다.




종류 \ 수행100회 (ns)1000회 (ns)10000회 (ms)20000회 (ms)30000회 (ms)50000회 (ms)
String274526907782953919104208.66710992.67
StringBuffer305514.7583808.323.66666766.333333
StringBuilder117393.7202339.72.3333332.3333333.3333337

내 맘대로 정리

  • String
    • 단순한 코드, 문자열의 불변성이 유지되는 코드에서 사용
  • StringBuffer
    • Thread-safe 환경에서 사용
  • StringBuilder
    • Single thread 환경에서 문자열 결합에서는 가장 좋은 성능을 보임

출처

아래의 글들을 교재삼아 작성하였습니다.

Share