Java/문자열

[Java]StringBuffer 클래스 사용 방법

DevStory 2022. 9. 10.

StringBuffer 클래스 사용 방법

Java에서 제공하는 StringBuffer 클래스는 멀티 스레드에서 안전한 클래스입니다. 여러 스레드가 StringBuffer 객체에 접근할 수 없기 때문입니다. StringBuffer 클래스는 문자열을 추가, 삭제, 변경할 수 있는 메서드를 제공하며, String 클래스보다 우수한 성능을 지니고 있습니다.

 

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


String 클래스의 문제점

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

 

위 내용을 이해하기 쉽게 다음 소스 코드를 확인해봅시다.

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

문자열 리터럴 방식을 사용하여 문자열 str을 문자열 "Hello"로 초기화했습니다. 그다음 문자열을 추가하기 위해 += 연산자를 사용하여 문자열 "World"를 추가한 다음 문자열 "!"를 추가합니다.

 

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

[동작 과정]

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

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

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

 

위 예제를 통해 String 클래스는 힙 영역에 생성된 기존 공간을 활용하지 않고 문자열을 추가, 수정, 삭제할 때마다 새로운 공간을 할당한다는 것을 설명했습니다.

 

즉, String 클래스는 문자열을 추가, 수정, 삭제할 때마다 새로운 공간을 할당한다는 것은 Heap 영역의 메모리 공간을 불필요하게 낭비한다는 것을 의미합니다. 따라서, 메모리 공간을 회수하는 작업을 수행하는 GC(가비지 컬렉션)가 처리해야 하는 작업이 많아집니다.

 

String 대신 StringBuffer를 사용하면, 문자열을 추가, 수정, 삭제할 때마다 새로운 공간을 할당하지 않으므로 메모리 공간을 효율적으로 사용할 수 있습니다. 

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

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

StringBuffer 클래스

StringBuffer 클래스는 Serializable, CharSequence 인터페이스를 구현하며, 최상위 클래스인 Object에서 파생된 클래스입니다.

public final class StringBuffer
    extends Object
    implements Serializable, CharSequence

 

StringBuffer 클래스의 객체는 new 키워드를 사용하여 생성할 수 있습니다. 버퍼(Buffer)를 활용하여 문자열을 문자 시퀀스(CharSequence)로 관리하며, 버퍼가 가득 차면 버퍼의 크기가 (기존 용량 + 1)의 두 배로 변경됩니다. 그리고 StringBuffer 클래스는 String Pool을 활용하지 않습니다.

 

다음 예제는 StringBuffer 객체를 생성 후 append() 메서드를 사용하여 문자열을 추가합니다.

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

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

 

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

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

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

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


StringBuffer 클래스 생성자

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


생성자 1. StringBuffer()

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

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

[실행 결과]

16

생성자 2. StringBuffer(int capacity)

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

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

[실행 결과]

5

생성자 3. StringBuffer(CharSequence seq)

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

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

[실행 결과]

21

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


생성자 4. StringBuffer(String str)

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

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

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

[실행 결과]

21

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


StringBuffer 클래스의 길이 및 용량

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

 

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

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

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

[실행 결과]

capacity: 16
length: 0

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

 

length() 메서드는 StringBuffer의 문자 시퀀스의 개수를 의미합니다. 위 예제는 StringBuffer 객체에 문자열을 할당하지 않았으므로 length() 메서드는 0을 반환합니다.

 

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

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

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

[실행 결과]

capacity: 21
length: 5

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

 

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

 

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

public static void main(String args[]) {
  StringBuffer sb = new StringBuffer("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

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

반응형

댓글