본문으로 건너뛰기

Item63

문자열 연결은 느리니 주의하라

1 개요

  • 문자열 연결 연산자인 + 는 여러 문자열을 하나로 합쳐주는 편리한 수단이다
  • + 연산자의 성능은 O(n^2)에 비례한다
  • String은 불변이기 때문에 String을 연결하기 위해서는 양쪽의 내용을 모두 복사해 새로운 String을 만들어내므로 성능 저하를 피할 수 없다

2 성능 측정

  • + 연산자, StringBuilder, StringBuffer를 이용해서 문자열을 연결해보고 성능을 측정해보자

2.1 + 연산자

@Test
void string_plus_operator() {
String name = "youngThree";
String result = "";

StopWatch stopWatch = new StopWatch("+ Operator");
stopWatch.start();
for (int i = 0; i < 100_000; i++) {
result += name;
}
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());
}
StopWatch '+ Operator': running time = 5559258559 ns

2.2 StringBuilder

@Test
void string_plus_StringBuilder() {
String name = "youngThree";
StringBuilder stringBuilder = new StringBuilder();

StopWatch stopWatch = new StopWatch("StringBuilder");
stopWatch.start();
for (int i = 0; i < 100_000; i++) {
stringBuilder.append(name);
}
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());
}
StopWatch 'StringBuilder': running time = 3960074 ns

2.3 StringBuffer

@Test
void string_plus_StringBuffer() {
String name = "youngThree";
StringBuffer stringBuffer = new StringBuffer();

StopWatch stopWatch = new StopWatch("StringBuffer");
stopWatch.start();
for (int i = 0; i < 100_000; i++) {
stringBuffer.append(name);
}
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());
}
StopWatch 'StringBuffer': running time = 5186206 ns

2.4 성능 비교

  • n = 100,000
시간
+ 연산자5559258559 ns
StringBuilder3960074 ns
StringBuffer5186206 ns

3 StringBuilder & StringBuffer

3.1 StringBuilder

  • 클래스 내부 버퍼에 문자열을 저장해 두고 추가, 수정, 삭제 작업을 할 수 있도록 설계되어 있다.
  • StringBuilder와 StringBuffer의 사용법은 동일하다
  • StringBuilder는 단일 스레드 환경에서만 사용하도록 설계되어 있다.
    • Thread Safe하지 않다.
    • StringBuffer는 Thread Safe하다
  • 버퍼가 부족할 경우 자동으로 버퍼의 크기를 늘리기 때문에 초기 버퍼의 크기는 그다지 중요하지 않다

3.2 StringBuffer

  • StringBuilder의 API와 같다고 볼 수 있다
  • 멀티 스레드 환경에서 사용할 수 있도록 동기화가 적용되어 있어 Thread Safe하다
  • 싱글 스레드에서만 쓰이는 StringBuffer는 StringBuilder로 대체하는 것이 성능상 유리하다

3.3 메서드 비교

  • 메서드에 synchronized 키워드를 적용 멀티 스레드 환경에서도 thread-safe하다

StringBuilder의 append 메서드

@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}

StringBuffer의 append 메서드

@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}

4 결론

  • 많은 문자열을 연결할 때는 문자열 연결 연산자 +를 피하고 StringBuilder를 사용하자
  • 추가적으로 thread-safe를 원한다면 StringBuffer를 사용하자