String은 immutable한 객체
즉 한 번 만들면 더 이상 그 값을 바꿀 수 없다는 뜻이다.
String test = "abc";
test += "def";
위와 같은 경우 보통은 하나의 객체에 값이 추가 되는 거라 생각하겠지만
String은 불변의 객체이기 때문에 문자열이 더해지는 순간 하나의 String 객체가 추가로 생성되는 방식이다.
기존 객체는 더 이상 사용할 수 없으며 Garbage가 되어 Garbage Collection에 의해 삭제되게 된다.
여기까지는 크게 어떤 문제가 생길지 감이 안 잡힐 수도 있다.
GC가 알아서 힙 메모리에 있는 Garbage를 알아서 삭제해주니까 별 문제 없지 않을까?
String 객체의 문자열이 빈번하게 바뀌는 경우를 생각해 보자.
String에 새로운 값이 추가, 삭제 등이 빈번하게 일어날 경우 힙 메모리에 임시 Garbage가 생성되어 전체적인 성능에 큰 영향을 끼칠 수가 있다.
이러한 비효율성을 해결하기 위한 클래스가 StringBuffer와 StringBuilder이다.
두 클래스는 문자열을 더하더라도 새로운 객체를 생성하지 않는다.
append() 라는 메소드를 이용하여 문자열을 더할 수 있다.
StringBuffer buffer = new StringBuffer();
buffer.append("string").append(" buffer").append(" test");
StringBuilder builder = new StringBuilder();
builder.append("string").append(" builder").append(" test");
System.out.println(buffer);
System.out.println(builder);
결과:
string buffer test
string builder test
그 이유는 Thread safe의 차이이다.
StringBuffer는 Thread safe 하지만
StringBuilder는 Thread safe 하지 않다.
속도는 Thread safe하지 않는 StringBuilder가 더 빠르다
※Thread safe란 어떤 함수나 변수, 혹은 객체가 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없음을 뜻한다
보통 하나의 메소드 내에서 문자열을 생성하여 더할 경우 StringBuilder를 사용해도 크게 상관없다. 오히려 더 빠르기 때문에 좋은 선택이라고 볼 수 있다.
하지만 특정 클래스에 문자열을 생성하여 더하기 위한 문자열을 처리하기 위한 인스턴스 변수가 선언됐고, 여러 스레드에서 이 변수를 접근하는 경우에는 반드시 StringBuffer를 사용해야 한다
String - 문자열 연산이 적고 멀티 쓰레드 환경일 때
StringBuffer - 문자열 연산이 많고 멀티 쓰레드 환경일 때
StringBuilder - 문자열 연산이 많고 단일 쓰레드 환경이거나 많은 수의 쓰레드가 접근하지않을 때