1. 글을 작성한 목표
Java에서 String 클래스는 불변성을 갖습니다.
따라서 변하지 않는 문자열을 자주 사용하는 경우엔 좋은 성능을 기대할 수 있습니다.
하지만 문자열에 대한 변경이 자주 일어나는 프로그램에서 String만 사용하게 된다면 효율적인 성능을 기대하기 어렵습니다.
이를 해결하기 위해 StringBuilder , StringBuffer을 사용하면 효율적인 성능을 보일 수 있습니다.
언제 StringBuilder를 사용하는지?, 또 언제 StringBuffer을 사용하는지? 에 대하여 살펴봅시다.
우선 String 부터 살펴봅시다.
2. String
문자열(String)은 자바 프로그램이 실행되는 동안 가장 많이 생성되는 객체라고 할 수 있습니다.
또한 문자열은 객체이지만 각각의 문자(char)의 나열로 구성되게 됩니다.
String을 구현하는 방식에는 2가지가 있습니다.
String a = “abc”; // 문자열, 문자의 배열
String b = “abc”; // 문자열, 문자의 배열
String c = new String(“abc”); // 비추하는 방식
System.out.println(a == b); // true
System.out.println(a == c); // false
우선 리터럴 값으로 할당하는 방식 먼저 살펴봅시다.
1) 리터럴 값으로 할당하기
String 을 리터럴 값으로 할당하는 경우엔 Heap 메모리 영역안의 특별한 메모리 공간인 String constant pool 에 저장됩니다.
만약 String constant pool에 존재하는 리터럴 값을 사용하게 된다면, 다시 새롭게 리터럴 값을 만들어 String constant pool에 저장하는 것이 아닌, 현재 존재하는 값을 재사용 하면 됩니다.
2) new 키워드를 통한 할당
new 키워드를 통해 String 변수에 값을 할당하게 되면 일반적인 객체와 동일하게 Heap 영역에 동적으로 메모리 공간이 할당되게 됩니다.
그림으로 살펴보면 다음과 같습니다.
3) 바이트 코드로 차이 살펴보기
컴파일된 Class 코드 또한 차이가 있다. 다음 코드를 살펴보자.
public void testString() throws java.lang.Exception;
Code:
0: ldc #89 // String abc
2: astore_1
3: return
public void testNewString() throws java.lang.Exception;
Code:
0: new #33 // class java/lang/String
3: dup
4: ldc #89 // String abc
6: invokespecial #93 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: return
위에 코드 방식이 리터럴로 할당한 방식이고, 아래의 방식이 new 연산자를 통해 할당하는 방식이다.
3. StringBuilder VS StringBuffer
Java에서 String 객체는 한번 값이 할당되면 그 공간은 변하지 않습니다.
따라서 String과 String을 더하면 새로운 String 객체를 생성해야만 합니다.
=> String과 String을 더하는 시점에 메모리 할당과 메모리 해제가 계속 발생하게 됩니다.
하지만 Stringbuilder나 StringBuffer 객체는 한번 값이 할당되더라도 String과 다르게 기존 데이터에 새로운 데이터를 더하는 방식을 취하기 때문에 속도가 더 빠릅니다.
여기서 할당된 공간이 변하지 않는 특성을 불변(Immutable)성이라고 하고,
할당된 공간이 변하는 특성을 가변(mutable)성이라고 합니다.
우선 Stringbuilder나 StringBuffer 모두 AbstractStringBuilder 라는 추상 클래스를 상속받고 있습니다.
AbstractStringBuilder 는 내부에 필드값을 3개 가지고 있습니다.
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
byte[] value;
/**
* The id of the encoding used to encode the bytes in {@code value}.
*/
byte coder;
/**
* The count is the number of characters used.
*/
int count;
value: 문자열 값들을 저장하는 배열
count: 문자열 길이값을 갖는 int형 변수
StringBuilder와 StringBuffer 클래스에 문자를 추가해줄 때는 append() 메서드를 사용면 됩니다.
append() 메서드는 AbstractStringBuilder 에 다음과 같이 구현되어 있습니다.
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
위와 같이 append 연산을 하면, 추가한 str의 길이만큼을 기존의 길이에 추가시키게 됩니다.
배열의 공간을 늘려주고, 빈 공간에 추가한 문자들이 들어가게 됩니다.
위에서 살펴본 내부동작을 통해 값이 변경되더라도 같은 주소공간을 참조하게 되며, 값이 변경되는 가변성을 나타냅니다.
1) 차이점
StringBuilder는 동기화를 지원하지 않는 반면, StringBuffer는 동기화를 지원하여 멀티 스레드 환경에서도 안전하게 동작할 수 있습니다.
StringBuffer는 메서드에서 synchronized 키워드를 사용하기 때문에 빠른 성능이 필요하다면 StringBuilder를 사용해야 합니다.
4. 요약
1. StringBuilder 를 사용 해야 할 때
StringBuilder는 동기화를 지원하지 않기 때문에 StringBuffer 보다 속도면에서 성능이 좋습니다.
=> 단일 스레드 환경 에서 문자열의 추가, 수정, 삭제 등이 빈번히 발생하는 경우 StringBuilder를 사용하는 것이 유리합니다.
2. StringBuffer 를 사용해야 할 때
StringBuffer는 동기화를 지원하여 멀티 스레드 환경에서도 안전하게 동작합니다.
=> 멀티 스레드 환경 에서 문자열의 추가, 수정, 삭제 등이 빈번히 발생하는 경우 StringBuffer를 사용하는 것 추천.
참고
'BackEnd > Java' 카테고리의 다른 글
[Java] equals, hashCode 를 같이 구현하는 이유 (0) | 2022.05.08 |
---|---|
[Java] Java 에서의 Thread, Light Weight Process (0) | 2022.03.30 |
[Java] Exception 기초 (0) | 2022.02.23 |
[Java] 람다와 익명클래스의 scope (0) | 2022.01.23 |
[Java] 자바 함수형 프로그래밍 (0) | 2022.01.21 |
댓글