String / StringBuilder / StringBuffer 차이점 & 성능 비교
자바에서는 대표적으로 문자열을 다루는 자료형 클래스로 String, StringBuffer, StringBuilder 라는 3가지 자료형을 지원한다.
하지만 세가지의 사용 용도에 따라 적합하게 사용하는 방법에 대해서 알아보고자 한다.
StringBuffer vs StringBuilder
위의 두 클래스는 주로 문자열을 연산(추가/변경)할 때 주로 사용하는 자료형이다.
String 자료형만으로도 +나 concat()을 사용하여 연산을 할 수 있다. 하지만 String 자료형을 사용하면 새로운 String 인스턴스를 생성하게 되며 이는 곧 공간의 낭비뿐 아니라 속도도 매우 느려지는 단점이 생기게 된다.
그래서 자바에서는 성능에 관계없이 문자열 연산을 전용으로 하는 자료형을 제공하는데 StringBuffer클래스는 내부적으로 Buffer라고 하는 독립적 공간을 가지게 되며, 문자열을 바로 추가할 수 있어 공간의 낭비도 없으며 문자열 연산 속도도 매우 빠르다는 특징이있다.
String msg = "";
msg += "hi ";
msg += "nice to ";
msg += "meet you";
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("hi " );
stringBuffer.append("nice to ");
stringBuffer.append("meet you");
System.out.println(msg);
System.out.println(stringBuffer.toString());
문자열 결과는 같지만 성능적으로 차이가 난다.
기본적으로 StringBuffer의 버퍼(데이터 공간) 크기의 기본값은 16개의 문자를 저장할 수 있는 크기이며, 생성자를 통해 그 크기를 별도로 설정할 수도 있다.
만일 문자열 연산중에 할당된 버퍼의 크기를 넘게 되면 자동으로 버퍼를 증강 시키니 걱정 안해도 된다. 다만, 효율이 떨어질 수 있으므로 버퍼의 크기는 넉넉하게 잡는 것이 좋다.
자주 쓰이는 StringBuffer클래스 내 메서드
substring(int idx, int idx2) : String
insert(int idx, String msg) : String
delete(int idx, int idx) : String
toString() : String
append(String msg) : String
length() : int
capacity() : int = StringBuffer 인스턴스의 버퍼크기 반환 (자료형의 할당된 크기를 반환)
reverse() : String
chatAt(int idx) : char
생성자
StringBuffer() = 버퍼의 길이를 지정하지 않으면 크기가 16 인 버퍼를 생성
StringBuffer(int length) = length 길이를 가진 StringBuffer 클래스의 인스턴스(buffer)를 생성
StringBuffer(String str) = 지정한 문자열( str )의 길이보다 16 만큼 더 큰 버퍼를 생성
*String Buffer 클래스 메서드와 StringBilder 클래스 메서드 사용법은 동일하다.
문자열 자료형의 불변과 가변
기본적으로 자바에서는 String 객체의 값은 변경할 수 없다.
이는 한번 할당된 공간이 별하지 않는다고 해서 '불변(immutable)' 자료형이라고 불린다. 그래서 초기 공간과 다른 값에 대한 연산에서 많은 시간과 자원을 사용하게 된다는 특징이 있다.
실제로 String 객체의 내부 구성 요소를 보면 다음과 같이 되어 있다.
인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 이 value 라는 인스턴스 변수에 문자형 배열로 저장되게 된다. 이 value라는 변수는 상수(final)형이니 값을 바꾸지 못하는 것이다.
public final class String implements java.io.Serializable, Comparable {
private final byte[] value;
}
새로운 인스턴스를 만들어 값을 참조하게 된다.
trim이나 toUpperCase 메서드 또한 또 다른 String 객체를 생성하여 리턴하는 것이다.
StringBuffer 와 StringBuilder는 메서드가 동일하다.
그러면 두개의 차이점은 무엇일까?
StringBuffer vs StringBuilder 차이점
사실 둘의 차이는 딱 한가지인데, 바로 Thread Safe한지 아닌지 차이 뿐이다
StringBuffer 클래스는 쓰레드에서 안전하다. (thread safe)
StringBuilder 클래스는 쓰레드에서 안전하지 않다. (thread unsafe)
Stringbuilder는 동기화를 지원하지 않는 반면, StringBuffer는 동기화를 지원하여 멀티 쓰레드 환경에서도 안전하게 동작할 수 있다.
StringBuffer는 메서드에서 synchronized 키워드를 사용하기 때문이다.
StringBuilder는 Thread safe하지 않아서 각기 쓰레드가 객체에 접근 해서 변경을 하면 기다려주지 않기 때문에 동시에 작업을 수행하다 몇번 씹혀서 제대로 수행이 안될 수 있다.
이와 달리 SltringBuffer는 멀티 쓰레드 환경에서, 한 쓰레드가 작업을 수행하는 경우 다른 쓰레드가 동시에 작업하지 못하도록 잠시 대기를 시켜주고 순차적으로 실행하게 한다. 이처럼 동시에 접근해 다른값을 변경하지 못하도록 하므로 Thread safe로써 정상적으로 원하는 결과를 얻을 수 있다.
그래서 web이나 소켓환경과 같이 비동기로 동작하는 경우가 많을 때는 StringBuffer를 사용하는 것이 안전하다는 것을 알 수가 있다.
3개의 클래스의 순수 성능 비교를 해보면 어떤게 제일 빠를까?
결론부터 말하면 기본 성능은 StringBuilder 클래스가 우월하다는 것을 알 수 있는데.
아무래도 쓰레드 안전성을 버린 StringBuilder가 좀 더 덜 따지고 연산을 하니 당연히 좀 더 빠를 수 밖에 없다.
그래서 만약 싱글쓰레드 환경에서나 비동기를 사용할 일이 없으면, StringBuilder를 쓰는 것이 이상적이라 할 수 있따.
하지만 현업에서는 자바 애플리케이션을 대부분 멀티스레드 이상의 환경에서 돌아가기 때문에 왠만하면 안정적인 StringBuffer로 통일하여 코딩하는 것이 좋다. 사실 두개의 차이는 미미하다.
참고