Java에서 String은 불변(immutable) 객체이다.
따라서 문자열을 + 로 더할 때마다 새로운 문자열 객체가 계속 만들어진다
String s = "a";
s += "b"; // 새로운 "ab" 문자열 생성
만약 반복해서 문자열을 더해 계속 새로운 문자열 객체를 만들게 되면 메모리 낭비와 성능 저하 문제가 발생할 수 있다. 이런 String의 단점을 보완하기 위해 나온 클래스가 StringBuilder와 StringBuffer이다.
두 클래스에서 제공하는 메서드는 동일한데, StringBuffer
는 Thread safe(동기화) 하다고 하며, String Builder
는 Thread safe 하지 않다고 한다.
두 클래스는 내부적으로 가변적인 문자열을 처리할 수 있도록 설계되었으며, append()
, insert()
, delete()
, reverse()
등 거의 동일한 메서드를 제공한다.
그러나 이 두 클래스 사이에는 중요한 차이점이 있다. 바로 동기화 처리 여부이다. StringBuffer
는 멀티스레드 환경에서 안전하게 사용할 수 있도록 모든 주요 메서드에 synchronized
키워드가 적용되어 있다. 따라서 여러 스레드가 동시에 접근하더라도 데이터 일관성이 보장되며, Thread-safe
하다.
반면, StringBuilder
는 synchronized
처리가 되어 있지 않기 때문에 Thread-safe
하지 않다. 그러나 그만큼 동기화 비용이 없기 때문에 싱글 스레드 환경에서는 StringBuffer
보다 훨씬 빠른 성능을 보인다.
결론적으로, 단일 스레드 환경에서는 StringBuilder
를 사용하는 것이 성능상 유리하고, 여러 스레드가 동시에 접근하는 멀티스레드 환경에서는 StringBuffer
를 사용하는 것이 안전하다. 최근에는 StringBuffer
대신 StringBuilder
를 사용하면서, 필요한 경우 외부에서 synchronized
블록을 적용하여 동기화를 직접 관리하는 방식이 선호되기도 한다.
<aside>
java 5 이상에서는 String의 더하기 연산을 할 경우, 컴파일 시 자동으로 해당 연산을 StringBuilder로 변환해준다. 따라서 일일이 더하는 작업을 변환해 줄 필요는 없지만 for 루프와 같이 반복 연산을 할 때에는 자동으로 변환을 해 주지 않기 때문에 반드시 사용해줘야한다.
</aside>
정리하면,
단일 스레드 환경 (ex. 일반적인 루프, 문자열 처리)에서는 StringBuilder
를 써서 속도를 높이는 게 좋고,
여러 스레드에서 동시에 접근할 수 있는 공유 객체라면 StringBuffer
로 안전하게 처리하는 게 좋다
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World!");
String result = sb.toString(); // 최종 문자열로 변환
System.out.println(result); // 출력: Hello World!