Java/문자열

[Java]StringBuilder 클래스 사용 방법

DevStory 2022. 9. 8.

StringBuilder 클래스 사용 방법

Java에서 제공하는 StringBuilder 클래스는 단일 스레드에서는 안전하지만, 멀티 스레드에서는 불안전한 클래스입니다. StringBuilder 클래스는 문자열을 추가, 삭제, 변경할 수 있는 메서드를 제공하며, String 클래스보다 우수한 성능을 지니고 있습니다.

 

이번 포스팅은 StringBuilder 클래스에 대한 설명과 사용 방법을 소개합니다.


String 클래스의 문제점

String 클래스는 불변(Immutable) 클래스입니다. 기존 문자열을 변경하지 않고 새로운 문자열을 생성하기 때문이죠.

 

예를 들어, 다음 소스 코드를 확인해봅시다.

public static void main(String args[]) {
  String str = "Hello";
  str += " World";
  str += "!";
}

문자열 "Hello"로 초기화된 문자열 변수 str에서 문자열 " World"를 추가한 다음 문자열 "!"를 추가합니다.

 

위 소스 코드의 동작 과정을 그림으로 나타내면, 다음과 같습니다.

[동작 과정]

순서 1. 문자열 리터럴 방식으로 문자열을 생성하면, JVM은 String Pool에 문자열 "Hello"가 존재하는지 확인합니다. String Pool에 문자열 "Hello"가 존재하지 않으므로 String Pool에 문자열 "Hello"를 저장합니다.

순서 2. += 연산자를 사용하여 문자열 변수 str에 문자열 " World"를 추가합니다. += 연산자를 사용하여 문자열을 추가하면, 문자열이 String Pool에 생성되지 않고 String Pool 외부 영역에 생성됩니다. 따라서, JVM은 문자열 "Hello World"가 존재하는지 체크하지 않습니다.

순서 3. += 연산자를 사용하여 문자열 변수 str에 문자열 "!"를 추가합니다. 두 번째 순서와 마찬가지로 String Pool 외부 영역에 문자열 "Hello Wolrd!"가 생성됩니다.

 

String의 문제는 문자열을 추가하거나 변경할 경우 Heap 영역에 메모리를 불필요하게 낭비한다는 것입니다. 불필요한 메모리 공간은 GC(가비지 컬렉션)에 의해 회수되지만, GC가 처리해야 하는 일이 많아집니다.

 

String 대신 StringBuilder를 사용하면, 하나의 메모리 공간만 할당되므로 메모리 공간을 효율적으로 사용할 수 있습니다.

※ + 연산자 및 concat() 메서드의 결과가 String Pool에 생성되지 않는 이유
JDK 1.5 버전 이후 + 연산자 또는 concat() 메서드를 사용하는 경우 StringBuilder로 컴파일되도록 하였습니다. StringBuilder는 String Pool이 아닌 String Pool 외부 영역에 문자열을 생성합니다.

이러한 이유로 위 예제에서 += 연산자를 사용하여 문자열을 추가했을 때, String Pool 영역에 문자열이 저장되지 않았던 것입니다.

StringBuilder 클래스

StringBuilder 클래스는 버퍼(Buffer)를 활용하여 문자열을 문자 시퀀스(CharSequence)로 관리합니다. StringBuilder의 버퍼가 가득 차면 버퍼의 크기가 (기존 용량 + 1)의 두 배로 변경됩니다. 그리고 StringBuilder 클래스는 String Pool을 활용하지 않습니다.

 

다음 예제를 살펴봅시다.

public static void main(String args[]) {
  StringBuilder sb = new StringBuilder("Hello");
  sb.append(" World");
  sb.append("!");
}

StringBuilder 클래스의 객체를 생성하고 생성자 함수를 사용하여 문자열 "Hello"를 초기합니다. 그리고 StringBuilder 클래스에서 제공하는 append() 메서드를 사용하여 문자열 " World"와 "!"를 추가합니다.

 

위 소스 코드의 동작 과정을 그림으로 설명하면 다음과 같습니다.

 

순서 1. Heap 영역에 새로운 공간을 생성하며, 문자열 "Hello"가 저장됩니다.

순서 2. append() 메서드를 사용하여 문자열 " World"를 추가합니다. 기존 공간에 문자열 " World"가 추가됩니다.

순서 3. append() 메서드를 사용하여 문자열 "!"를 추가합니다. 기존 공간에 문자열 "!"가 추가됩니다.

 

String과 달리 불필요한 메모리 공간을 불필요하게 생성하지 않습니다. StringBuilder는 하나의 공간으로 문자열을 관리하기 때문입니다.


StringBuilder 클래스 생성자

StringBuilder 클래스는 네 가지 형태의 생성자 함수를 가지고 있습니다.


생성자 1. StringBuilder()

매개변수가 없는 StringBuilder() 생성자 함수는 StringBuilder 객체의 초기 용량을 16Byte로 설정합니다.

public static void main(String args[]) {
  StringBuilder sb = new StringBuilder();
  System.out.println(sb.capacity());
}

[실행 결과]

16

생성자 2. StringBuilder(int capacity)

int 타입의 값을 StrintBuilder 생성자 함수에 전달하면, StringBuilder 객체의 초기 용량을 설정할 수 있습니다.

public static void main(String args[]) {
  StringBuilder sb = new StringBuilder(5);
  System.out.println(sb.capacity());
}

[실행 결과]

5

생성자 3. StringBuilder(CharSequence seq)

String 대신 CharSequence를 StringBuilder 생성자 함수에 전달할 수 있습니다.

public static void main(String args[]) {
  CharSequence seq = "Hello";
  StringBuilder sb = new StringBuilder(seq);
  
  System.out.println(sb.capacity());
}

[실행 결과]

21

CharSequence를 StringBuilder 생성자 함수에 전달하는 경우 StringBuilder 객체의 초기 용량은 (매개변수로 전달된 CharSequence의 길이 + 16)입니다.


생성자 4. StringBuilder(String str)

StringBuilder 생성자 함수에 문자열을 전달할 수 있습니다.

public static void main(String args[]) {
  StringBuilder sb = new StringBuilder("Hello");

  System.out.println(sb.capacity());
}

[실행 결과]

21

String을 StringBuilder 생성자 함수에 전달하는 경우 StringBuilder 객체의 초기 용량은 (매개변수로 전달된 String의 길이 + 16)입니다.


StringBuilder 클래스의 길이 및 용량

StringBuilder 클래스에서 제공하는 length() 메서드와 capacity() 메서드는 비슷해 보이지만, 완전히 다른 기능을 수행합니다.

 

다음 예제는 매개변수가 없는 기본 생성자 함수로 StringBuilder 객체를 생성 후 capacity() 메서드와 length() 메서드를 호출합니다.

public static void main(String args[]) {
  StringBuilder sb = new StringBuilder();

  System.out.println("capacity: " + sb.capacity());
  System.out.println("length: " + sb.length());
}

[실행 결과]

capacity: 16
length: 0

capacity() 메서드는 StringBuilder의 버퍼 크기입니다. StringBuilder의 객체가 기본 생성자 함수로 생성된 경우 초기 용량은 16Byte입니다. 만약, 버퍼의 용량이 가득 차면 (기존 용량 + 1) * 2로 버퍼의 크기를 변경합니다.

 

length() 메서드는 StringBuilder의 문자 시퀀스의 개수를 의미합니다. 위 예제에서 StringBuilder에 할당 또는 추가된 문자열이 존재하지 않으므로 length() 메서드는 0을 반환합니다.

 

다음 예제는 String을 매개변수로 가지는 생성자 함수로 StringBuilder 객체를 생성 후 capacity() 메서드와 length() 메서드를 호출합니다.

public static void main(String args[]) {
  StringBuilder sb = new StringBuilder("Hello");

  System.out.println("capacity: " + sb.capacity());
  System.out.println("length: " + sb.length());
}

[실행 결과]

capacity: 21
length: 5

String 또는 CharSequence 객체를 StringBuilder 생성자 함수에 전달한 경우 StringBuilder 객체의 버퍼 크기는 (매개변수로 전달된 문자열의 길이 + 16)으로 설정됩니다.

 

문자열 "Hello"의 길이는 5이므로 StringBuilder 객체의 버퍼 크기는 (5 + 16) = 21로 설정됩니다. 그리고 문자열 "Hello"의 길이가 5이므로 length() 메서드는 5를 반환합니다.

 

다음 예제는 append() 메서드를 사용하여 StringBuilder 객체의 버퍼 크기를 변경합니다.

public static void main(String args[]) {
  StringBuilder sb = new StringBuilder("Hello");

  System.out.println("[버퍼 크기 초과 전]");
  System.out.println("capacity: " + sb.capacity());
  System.out.println("length: " + sb.length());

  sb.append(" World! Hello JavaScript!");

  System.out.println("\n[버퍼 크기 초과 후]");
  System.out.println("capacity: " + sb.capacity());
  System.out.println("length: " + sb.length());
}

[실행 결과]

[버퍼 크기 초과 전]
capacity: 21
length: 5

[버퍼 크기 초과 후]
capacity: 44
length: 30

StringBuilder의 객체인 sb는 초기 버퍼 크기가 21입니다. append() 메서드를 사용하여 25개의 문자열을 추가하면, 버퍼 크기를 초과하므로 버퍼의 크기는 (기존 버퍼 크기(21) + 1) * 2 = 44로 변경됩니다.

반응형

댓글