StringBuffer와 StringBuilder

StringBuffer와 StringBuilder에 대한 탐구

Java에는 기본적으로 제공하는 api들이 있다. 이에 대한 내용은 오라클 공식 문서에 자세히 기재되어 있다.
java의 기본 패키지들은 따로 import 하지 않아도 기본적으로 사용할 수 있기 떄문에 이를 잘 활용하는 것이 필요하다.

본 포스팅에서는 String, StringBuffer, StringBuilder에 대해 알아보려고 한다.

java.lang.StringBuffer
  • java.lang package에 포함되어 있는 String, StringBuffer, StringBuilder classes

String

java.lang.String

먼저 String에 대한 문서 내용을 확인해보면 Serializable, Comparable, CharSequence 인터페이스가 상속되어 있고, public final class로 되어 있다. serialize가 가능하며 문자열이고 비교가능한 값이라는 것을 알 수 있다. 또한 final class이기 때문에 String class를 상속받을 수는 없다.

Constant Pool

Java에서는 String을 특별하게 취급한다. Java heap 영역 안에 String constant pool이 존재하는데, String을 literal로 생성하면 Heap 영역 안에 있는 constant pool에 저장되어 재사용된다.

그림으로 나타내면 아래와 같다.

java heap 구조

String 객체를 생성하여 저장하면 중복되는 값도 계속 heap 영역에 저장되기 때문에 메모리 낭비가 생긴다. 따라서 중복되는 문자열은 literal로 생성하여 constant pool에 저장되게 하는게 유리하다. constant pool에 저장된 문자열은 같은 주소값을 참조한다는 점도 참고하여 코드 설계에 적용하는것이 좋다.

추가로 String 객체를 생성하여 intern() 메소드를 호출하면 constant pool에 접근하여 이미 있는 문자열을 참조할 수 있다.

StringBuilder description

그리고 문자열을 더할때는 StringBuffer 또는 StringBuilder를 사용하여 구현한다고 한다.
왜 단순히 문자열에 계속 문자를 더하지 않고, StringBufferStringBuilder을 이용하는 것일까?
좀 더 자세히 알아보자.

StringBuffer

StringBuffer는 문자열을 추가하거나 변경 할 때 주로 사용하는 자료형이다.
실제로 어떻게 다른지 확인해보자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 문자열 test1의 값과 주소값 확인
String test1 = "TEST 1";

System.out.println("test1: " + test1);
System.out.println("test1 reference: " + test1.hashCode());

// 문자열에 한 글자씩 더하여 주소값 확인
for (int a = 0; a = 3; a++) {
    test1 += a;
    System.out.println("test1: " + test1);
    System.out.println("test1 reference: " + test1.hashCode());
}

// StringBuffer 생성
StringBuffer sb = new StringBuffer();

// StringBuffer에 문자를 append 하고 주소값 확인
for (int a = 0; a = 3; a++) {
    sb.append(a);
    System.out.println("sb: " + sb);
    System.out.println("sb reference: " + sb.hashCode());
}

실행 결과

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
test1: TEST 1
test1 reference: -1823841245
test1: TEST 10
test1 reference: -704503699
test1: TEST 101
test1 reference: -364778140
test1: TEST 1012
test1 reference: 1576779598
sb: 0
sb reference: 471910020
sb: 01
sb reference: 471910020
sb: 012
sb reference: 471910020

위의 경우처럼 단순히 문자열에 한 글자를 더하게 되면, 새로운 주소값을 갖는 새 문자열을 생성하여 저장하게 된다. 이렇게 문자열에 문자를 반복적으로 수정이 이뤄지는 상황은 비효율적이다. 이러한 경우 StringBuffer를 사용하여 문자를 append 해주면 같은 주소값을 참조하여 하나의 문자열에 계속 추가를 해주게 되어 더 효율적인 것이다.

그럼 String에 새로운 문자를 추가하면 왜 새로운 문자열을 만들어 버리는것일까?

여기서 알아야될 것은 사실 String은 char형의 배열 형태라는 점이다. Stringprivate final char value[]; 라고 선언이 되어있어서 한 번 만들어지면 변경이 불가하다. 때문에 새로운 문자를 추가하면 새로운 문자열로 만들어지는 것이다.

StringBuilder와 StringBuffer

그렇다면 StringBuilder는 또 무엇인가? StringBuilderStringBuffer 둘 다 변경 가능한 문자열이지만 차이점이 존재한다.

StringBuilder

  • synchronization 적용되지 않음(비동기적)
  • 단일 스레드에서 StringBuffer보다 연산처리가 빠르다.

StringBuffer

  • synchronization 적용(동기적)
  • 여러 스레드에서 사용하기에 안전

싱글 스레드 환경에서 StringBuilder와 StringBuffer의 성능을 직접 비교해보면 다음과 같다.

 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
StringBuffer sBuffer = new StringBuffer();
StringBuilder sBuilder = new StringBuilder();

new Thread(() -> {
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        sBuffer.append(i);
    }
    long endTime = System.currentTimeMillis();

    // StringBuffer에 1천만번 append를 한 시작 시간과 끝난 시간 비교
    System.out.println("StringBuffer -> " + (endTime - startTime));
    
}).start();

new Thread(() -> {
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        sBuilder.append(i);
    }
    long endTime = System.currentTimeMillis();

    // StringBuilder에 1천만번 append를 한 시작 시간과 끝난 시간 비교
    System.out.println("StringBuilder -> " + (endTime - startTime));
    
}).start();

실행 결과

1
2
StringBuilder -> 637
StringBuffer -> 849

하나의 스레드에서는 StringBuilder가 조금 더 빠른것을 확인할 수 있다.

결론적으로 하나의 스레드라면 지역 변수로 StringBuilder를, 여러개의 스레드라면 전역 변수로 StringBuffer를 사용하는 것이 효과적이다.



References

오라클 api 공식 문서
Java에서 String, StringBuilder, StringBuffer의 차이
String, StringBuffer, StringBuilder

Choi SeungEun Tech Blog
Built with Hugo
Theme Stack designed by Jimmy