이번 포스트를 보기 앞서 이전 포스트를 보고 오시기 바랍니다.
https://jaystorage.tistory.com/20
String 과 StringBuilder, StringBuffer 의 차이점
String과 (StringBuilder, StringBuffer)는 문자열을 다루는 클래스라는 점에서 공통점을 가지고 있지만 String은 불변성(immutable)인 반면 (StringBuilder, StringBuffer)는 가변성(mutable)이라는 차이점이 있다.
일반적으로 단순한 문자열 연산을 할 때는 String 에 + 나 concat() 메소드를 사용해 문자열을 변경한다. 하지만 문자열 연산이 빈번히 일어날 때 String 객체를 사용할 경우 다음과 같은 문제점이 발생한다.
* String은 불변성(immutable) 객체라 문자열을 변경하는 모든 연산은 새로운 String 객체를 생성한다.
ex) "Hello" + " World" 는 "Hello" String 객체와 " World" String 객체를 합쳐서 새로운 String 객체 "Hello World"를 생성한다. 이럴 경우 기존의 "Hello" String 객체와 " World" String 객체는 GC의 대상이 되고 GC가 이들 객체를 수집하기 전까지 메모리에 남아있게 된다.
반면 (StringBuilder, StringBuffer)는 내부적으로 버퍼(buffer)라는 독립적인 공간에 문자열을 저장하고 관리하기 때문에 String보다 효율적인 문자열 연산을 할 수 있다.
StringBuilder와 StringBuffer의 문자열 연산이 효율적인 이유
앞서 StringBuilder와 StringBuffer는 내부적으로 버퍼(buffer)라는 독립적인 공간에 문자열을 저장하고 관리하기 때문에 String보다 효율적인 문자열 연산이 가능하다고 했다. 그렇다면 이것이 어떻게 가능한지 StringBuilder와 StringBuffer를 구체적으로 알아보자
(아래 코드는 java 17 기준)
StringBuilder와 StringBuffer는 공통적으로 AbstractStringBuilder를 상속 받고 있는데 AbstractStringBuilder는 다음과 같은 필드를 가지고 있다.
각 필드의 역할은 다음과 같다.
- value : 문자열을 저장하는 byte형 배열
- java 8에서는 char 배열이었으나 java 9 에서부터 byte 배열로 변경됨 - coder : byte 문자열의 인코딩 정보
- count : 현재 문자열 길이를 나타내는 int 형 변수
위에서 StringBuilder와 StringBuffer는 내부적으로 버퍼(buffer)라는 독립적인 공간에 문자열을 저장하고 관리한다고 언급했는데 value 필드가 바로 이 버퍼를 의미한다.
StringBuilder나 StringBuffer는 생성될 때 버퍼의 기본 length를 16으로 설정한다. 만약 StringBuilder나 StringBuffer의 문자열이 변경되다가 정해진 length를 넘어가게 되면 기존 length의 2배가 되는 새로운 byte 배열을 생성하여 문자열을 저장한다.
따라서 String 에 비해 StringBuilder나 StringBuffer가 더 효율적인 이유는 byte 배열의 길이를 넘어가지 않는 수준에서 문자열에 변화가 일어나면 굳이 새로운 배열을 생성하지 않기 때문이다.
StringBuilder와 StringBuffer의 차이점
두 클래스는 문법이나 배열구성도 모두 같지만 동기화(Synchronization)에서의 지원의 유무가 다르다. StringBuilder는 동기화를 지원하지 않는 반면 StringBuffer는 동기화를 지원하여 멀티 스레드 환경에서도 안전하게 동작할 수 있다.
그 이유는 StringBuffer는 메소드에서 synchronized 키워드를 사용하기 때문이다.
* synchronized 키워드는 여러 개의 스레드가 한 개의 자원에 접근하려고 할 때, 현재 데이터를 사용하고 있는 스레드를 제외하고 나머지 스레드들이 데이터에 접근할 수 없도록 막는 역할을 수행한다.
예를 들어 멀티스레드 환경에서 A 스레드와 B스레드 모두 같은 StringBuffer 클래스 객체 sb의 append() 메서드를 사용하려고 하면, 다음과 같은 절차를 수행하게 된다.
- A 스레드 : sb의 append() 동기화 블록에 접근 및 실행
- B 스레드 : A 스레드 sb 의 append() 동기화 블록에 들어가지 못하고 block 상태가 됨
- A 스레드 : sb의 append() 동기화 블록에서 탈출
- B 스레드 : block 에서 running 상태가 되며 sb 의 append() 동기화 블록에 접근 및 실행.
StringBuilder 클래스 내부를 보면 동기화가 필요할 경우 StringBuffer을 추천한다는 문구를 확인할 수 있다.
'Java' 카테고리의 다른 글
동일성(identity)과 동등성(equality) (1) | 2024.06.07 |
---|---|
깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy) (0) | 2024.05.31 |
BigDecimal (0) | 2024.05.20 |
String과 String Constant Pool (0) | 2024.03.26 |
상수(Constant)와 리터럴(Literal) (0) | 2024.03.17 |