상세 컨텐츠

본문 제목

String vs StringBuffer vs StringBuilder

Java

by AyaanDev 2023. 4. 3. 23:13

본문

반응형

Java에서는 같은 String이라도 String,StringBuffer,StringBuilder와 같이 비슷해보이는 다양한 클래스를 제공한다.

위 3가지 클래스를 비교해보고 어떤 차이점이 있는지 알아보자.

궁금증?

필자는 스터디중 아래와 같은 코드를 만나게 됐다.

    String myName = "jinwoo";
    String yourName;
    yourName = myName;
    System.out.println(yourName);

    yourName = "Ayyan";
    System.out.println(yourName); 
    System.out.println(myName);

위 코드를 본 필자는 myName과 yourName은 참조변수이기 때문에
1. yourName은 myName과 함께 "jinwoo" 문자열이 저장된 메모리공간을 가리키고 있을것이다.
2. yourName="Ayaan"을 넣었기 때문에 myName, yourName 모두 Ayaan을 가리키고 있을것이다.

그림으로 표현하자면 아래와 같을것이라 생각했다.

jinwoo  
Ayaan  
Ayaan  

따라서 콘솔에는 위와 같이 출력될 것이라 생각했다.

하지만 실제 콘솔을 찍어본 결과는 아래와 같다.

jinwoo
Ayyan
jinwoo

Immutable class(변경 불가능한 클래스)

참조변수는 메모리 공간의 주소값을 가지고있다고 배웠으며, 이에 따르면 위의 생각이 맞을텐데 왜 저런 결과가 나오는지 이해가 안되었다. 몇일 후 자바의 정석을 읽다가 String클래스에 대한 설명에서 그 해답을 찾을 수 있었다.

public final class String implements java.io.Serializable, Comparable {
    private char[] value;
}

String 클래스에는 문자열을 저장하기 위해서 문자형 배열 변수 (char[]) value를 인스턴스 변수로 정의해놓고 있다.

인스턴스 생성 시 생성자의 매개변수로 전달받은 문자열은 이 인스턴스 변수에 문자형 배열로 저장되는 것이다.

한번 생성된 String인스턴스가 갖고 있는 문자열은 읽어올 수만 있고, 변경할 수는 없다.

예를 들어 +연산자를 이용하여 문자열을 결합하는 경우를 확인해보자.

String a = "A";
String b = "B";
String a = a+b;

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

a에 "AB"가 할당될 때, "A"가 "AB"로 대체되는 것이 아니라 "AB"를 위한 메모리 공간을 새로 할당하고 이를 변수a가 가리킨다.

덧셈연산자 +를 사용해서 문자열을 결합하는 것은 매 연산 시 마다 새로운 문자열을 가진 String인스턴스가 생성되어 메모리 공간을 차지하게 되므로 가능한 한 결합횟수를 줄이는 것이 좋다.

문자열 간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는 String클래스 대신 StringBuffer클래스를 사용하는 것이 좋다. StringBuffer인스턴스에 저장된 문자열은 변경이 가능하므로 하나의 StringBuffer인스턴스 만으로도 문자열을 다루는 것이 가능하다.

StringBuffer 생성자

// (1)
public StringBuffer (int length){
    value=new char[length];
    shared=false;
}

// (2)
public StringBufer(){
    this(16);
}

// (3)
public StringBuffer(String str){
    this(str.length()+16);
    append(str);
}

(1)에서 length를 통해 버퍼의 크기를 결정할 수 있다.

(2) 기본생성자를 통해 StringBuffer 인스턴스를 생성할 경우 기본 크기 16으로 생성된다.

(3) String변수를 넘겨 생성할 경우 str크기 +16의 크기로 생성된다.

 

만약, 버퍼의 크기를 넘기는 문자열을 저장하려 하면 어떻게 동작할까?

// 새로운 길이의 배열을 생성한다.
char newValue[] = new char[newCapacity]

System.arrayCopy(value,0,newValue,0,count); // value배열의 값을 newValue배열로 복사한다.
value=newValue; // 새로 생성된 배열의 주소를 참조변수 value에 저장.

메모리를 새로 할당받고 기존 배열의 값을 새로운 배열에 복사하는 과정이 일어나기 때문에 이는 리소스를 많이 잡아먹을 수 있다. 따라서 StringBuffer인스턴스를 생성할 때는 (1)생성자를 이용해 처음부터 충분히 여유있는 크기로 지정하는 것이 좋다.

 

StringBuffer 변경 vs String 변경

// ****** String 변수 값 변경 *********/
String a="testA"
String a= a + " testB"


// ****** String 변수 값 변경 *********/
StringBuffer a = new StringBuffer("testA");
a.append(" testB");
<String 변수의 값 변경> <StringBuffer 변수의 값 변경>

String변수는 새로 메모리공간을 할당받아야 하는 반면, StringBuffer변수는 기존 배열을 활용해 값을 변경한다.

 

참고:

StringBuffer a=new StringBuffer("testA");는 생성자 (3)을 이용하므로 크기가 21인 배열이 할당된다. 위 그림에서는 그림의 크기를 고려해 그림의 일부만 보여지고있다.

 

StringBuffer vs StringBuilder

StringBuffer는 멀티스레드에 안전(thread Safe)하도록 동기화되어 있다. 이러한 동기화 작업은 StringBuffer의 성능을 떨어뜨린다. 멀티쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능을 떨어뜨리는 것이다.

 

StringBuilder는 StringBuffer에서 동기화부분만 사라진 클래스로 이해하면 된다. 따라서 thread Safe해야할 필요가 없다면 StringBuilder Thread Safe해야하는 환경일 경우 StringBuffer를 선택하면 된다.

반응형

'Java' 카테고리의 다른 글

객체 지향 설계의 5원칙(SOLID)  (0) 2023.05.30
[JAVA] Singleton 패턴에 대해 알아보자  (0) 2023.04.11
동등성(Equality) VS 동일성(Identity)  (0) 2023.02.15

관련글 더보기

댓글 영역