본문 바로가기
study_algorithm/문제풀면서 알게된 것들

String, StringBuffer, StringBuilder

by developer_j 2020. 11. 28.
728x90
반응형

이전 프로젝트에서 별 생각없이 "Text1" + "Text2" 형태로 +연산자를 통해 문자열을 합치곤 했다. 그러나 String 이외에도 StringBuilder, StringBuffer 클래스가 있다는 것을 알게 되었고 각 클래스의 차이점이 궁금해졌다.
StringBuilder나 StringBuffur, String이 따로 있는 이유가 있지 않을까?

우선, String 과 StringBuffer/StringBuilder 클래스의 가장 큰 차이점은 String 은 불변(immutable), StringBuffer/StringBuilder 은 가변(mutable)의 속성을 가진다는 점이다.

1. String

String 은 불변 객체다.
String 은 리터럴(literal), 또는 new 생성자를 통해 값을 할당할 수 있으며 처음에 할당한 값에 따라 크기가 고정된다. 즉, 문자열을 수정할 때마다 새로운 문자열을 생성하고 리턴한다.

  • String의 초기값 할당에 리터럴을 이용하게 된다면 String constant pool(상수 영역)이라는 영역에 올라가게 된다. 이 때, 내부적으로 String 의 intern() 메소드가 호출되는데 intern() 메소드는 주어진 문자열이 String constant pool에 존재하는지 검색한다. 있다는 그 주소값을 반환하고, 없다면 새로 등록한다!
  • new 연산자를 이용하게 된다면 heap 영역에 새로운 객체로 올라가게 된다.
    heap영역이나 상수풀 영역은 차후 더 자세하게 살펴보기로 하고, 지금은 서로 다른 String 을 생성하거나 String 간의 + 연산자를 통해 합쳐진 String 은 새로 메모리 영역에 올라간다고 생각하면 된다.

즉,

String str = "hello";    //"hello"
str = str + " world";    //"hello world"

라는 코드가 있을 때, str가 담고있는 내용이 기존의 "hello"에서 "hello world"로 변경된 것처럼 보이지만 그게 아니라는 것이다.

"hello" , "hello world"라는 두 개의 공간이 있게 되고, 기존에 "hello"를 가리키던 str이 "hello world"를 가리키게 되며 "hello"는 가비지콜렉터(GC)에 의해 사라지게 된다.

따라서 String 을 통한 문자열 연산은 권장되지 않는다. 변하지 않는 문자열을 자주 읽어들이는 것에는 좋지만, 자주 문자열을 변경한다면 계속해서 새로 합쳐진 문자열과 남아있는 기존 문자열들이 생성되기 때문이다. 메모리를 관리하는 가비지 콜렉터가 할 일이 늘어날 수록, 성능에 영향을 끼치게 된다.

+) String 은 왜 불변객체이며 새로 생성되는가? 에 대한 내용은 여기에서 참조한다. 요약하면, String 은 char[] 배열로 구성되고 char는 String 클래스 내에서 private final char형으로 선언됨. 따라서 외부에서 접근이 불가능하므로 불변한 클래스가 된다.

2. StringBuilder / StringBuffer

이를 해결하기 위한 것이 가변성을 가지는 StringBuilder와 StringBuffer다. append(), delete() 를 통해 동일 객체 내에서 문자열 변경이 가능.
동일 객체를 "변경"하는 것이니 새로운 문자열 객체를 "생성"하는 String 대신 사용하는 것이 성능 향상에도 도움이 된다.

그렇다면 StringBuilder / StringBuffer 의 차이는 무엇인가? 바로 동기화 유무이다.

StringBuffer는 동기화(synchronized)를 지원하여 멀티쓰레드 환경에서 안전(thread-safe)하다. 참고로 String도 불변성을 가지므로 thread-safe 하다.
반대로 StringBuilder는 동기화를 지원하지 않으므로 단일쓰레드 환경에 적합하다.
동기화를 고려하지 않는 환경이라면 StringBuilder가 성능이 더 좋고, 동기화가 필요한 환경이라면 StringBuffer를 사용하는 것이 더 좋다고 하는데 ... 문자열 연산이 엄청나게 일어나는 것이 아니라면, 또는 쓰레드에 안전한지 모를 경우 StringBuffer를 사용해서 더 안정적으로 사용하는 것이 낫다고 한다.
결론적으로 스레드 안전 여부에 관련없이 개발할거라는 확신이 있다면 StringBuilder를, 스레드에 안전해야 되거나 안전에 신경써야되는지 모르겠다면 StringBuffer를 사용한다.

StringBuilder나 StringBuffer는 처음 생성될 때 크기를 설정한다.
StringBuilder sb = new StringBuilder() --> 기본으로 16 char의 용량이 설정됨
StringBuilder sb = new StringBuilder("test") --> 기본 16 + 4 = ()안의 데이터만큼 추가 용량이 설정됨

stringBuilder.capacity()를 사용하면 크기를 알 수 있다.

3줄 요약

  • String 과 StringBuffer/StringBuilder 클래스의 가장 큰 차이점은 String 은 불변(immutable), StringBuffer/StringBuilder 은 가변(mutable)의 속성을 가진다는 점이다.
  • 똑같은 문자열을 단순조회만 할 때는 String 이 적합하나, 문자열 변경이 잦다면 StringBuffer 또는 StringBuilder를 사용한다.
  • StringBuffer와 StringBuilder는 멀티쓰레드 안전을 고려해야하는 지에 따라 사용여부를 결정한다. 멀티쓰레드 안전 필요 시 StringBuffer, 아니라면 StringBuilder.
  • 대부분의 글에서 StringBuffer 사용이 안전 상 더 좋다고 권유한다.

원래 내가 찾아보았던 내용은 아래 블로그 글이었다.
https://mygumi.tistory.com/83
대용량 데이터 출력하는 데에, System.out.println과 StringBuilder를 활용해 출력하는 것.
막연하게 for문 안쪽에서 수만번 데이터를 System.out.println으로 출력하는 것보다,
StringBuilder로 append시켜놓고 한번에 System.out.println으로 출력하는 것이 시간이 훨씬 적게 걸린다는 것이다.

여기서 StringBuilder와 StringBuffer 차이를 보다가 여기까지 오긴 했지만.. 어쨌든 오늘도 매우 유익한 공부시간이었다.

더 찾아 보고 싶은 내용 : 주소값과 메모리영역, 자바 버전에 따라 바뀐 메모리 영역, hashCode()

참조 링크
https://madplay.github.io/post/java-string-literal-vs-string-object
https://ifuwanna.tistory.com/221
https://jeong-pro.tistory.com/85
https://12bme.tistory.com/42
https://coding-factory.tistory.com/546

728x90
반응형