[Java] String vs StringBuilder vs StringBuffer
String
String 인스턴스 생성 방법에는 다음과 같이 두 가지가 있다.
String str1 = new String("java")
String str2 = "java"
두 방법으로 생성된 인스턴스는 약간의 차이가 있다.
다음 생성된 String 인스턴스들을 살펴보자
String str1 = new String("java")
String str2 = new String("java")
String str3 = "java"
String str4 = "java"
생성된 String 인스턴스들을 ==을 사용해 비교해 보면 str1과 str2는 다른 인스턴스를 참조하지만 str3와 str4는 같은 인스턴스를 참조한다는 것을 알 수 있다.
왜 이런 차이가 발생할까?
String 인스턴스는 Immutable한 인스턴스이기 때문이다.
str3나 str4처럼 String literal로 생성하면 문자열은 힙영역의 String Pool에 저장되고 해당 문자열을 참조한다. 만약 문자열이 같은 인스턴스가 생성된다면 인스턴스는 String Pool에 있는 문자열을 공유하여 참조하게 된다.
반면 str1이나 str2처럼 new 연산자를 사용해 생성하면 문자열은 힙영역에 저장되고 해당 문자열을 참조한다. 문자열이 같은 인스턴스가 생성된다면 인스턴스는 문자열을 공유하지 않고 새로운 문자열을 힙영역에 생성하여 참조한다.
이런 Immutable한 성질로 인해 문자열 결합에서 어떤 특성을 가지게 된다.
String str = "java" //java
str = str.concat(" hi") //java hi
String 인스턴스가 concat 연산을 사용해 문자열을 결합하면 str이 참조하는 문자열인 "java" 값이 변경되는 것이 아니라 메모리에 새로운 "java hi" 값이 생성되고 해당 값을 str이 참조하게 된다. 즉, String 인스턴스의 값이 변경되면 참조하는 값이 변하는 것이 아닌, 새로운 값을 생성하고 그 값을 인스턴스가 참조하게 되는 것이다.
String의 불변성은 변하지 않는 문자열을 자주 읽어들이는 경우 사용하면 좋은 성능을 기대할 수 있으나, 문자열 추가, 수정, 삭제 등의 연산이 빈번할 경우 String을 사용하면 힙영역에 많은 Garbage가 생성되어 성능에 치명적인 영향을 줄 수 있다.
이를 위해 Java에서는 mutable한 성질을 가지는 StringBuilder와 StringBuffer 클래스가 생겨나게 되었다.
StringBuilder
StringBuilder 클래스는 String 클래스와 달리 참조된 값을 변경할 수 있다. 또한 StringBuilder 인스턴스는 문자열 관리를 위한 메모리 공간을 스스로 관리한다.
StringBuilder sb = new StringBuilder("java") //java
sb = sb.append(" hi") //java hi
StringBuilder 인스턴스가 append 연산을 사용한다면 sb가 참조하는 "java" 값이 "java hi" 로 변경된다.
따라서 문자열의 추가, 수정, 삭제 등이 빈번하게 발생한다면 String이 아닌 StringBuilder 클래스를 사용하는 것을 권장한다.
StringBuffer
StringBuffer 클래스는 StringBuilder와 거의 같은 클래스이지만 차이점이 하나 존재한다.
그 차이점은 바로 동기화(Synchronization)의 유무, 즉 멀티쓰레드 환경에서의 안정성이다.
StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전하지만 단일쓰레드에서는 속도가 느리다. 반면, StringBuilder는 동기화를 지원하지 않으므로 멀티쓰레드 환경에서 안전하지않지만 단일쓰레드에서의 속도는 비교적 빠르다.
추가적으로 String도 멀티쓰레드 환경에서 안전하다.
참조
윤성우의 열혈 JAVA 프로그래밍
https://gbsb.tistory.com/255
https://ifuwanna.tistory.com/221