Java 문자열 대표 클래스
- Java에서 문자열을 다루는 대표적인 클래스는 String, StringBuilder, StringBuffer 가 있다.
String vs StringBuilder, StringBuffer
- String과 StringBuilder, StringBuffer 클래스의 가장 큰 차이점은 String은 불변(Immutable)의 속성을 갖는다는 점이다.
- String 객체는 한 번 값이 할당되면 그 공간은 변하지 않는다.
- StringBuilder과 StringBuffer 객체는 한 번 값이 할당되더라도 한 번 더 다른 값이 할당되면 할당된 공간이 변하는 특성을 갖고 있다.
- 여기서 할당된 공간이 변하지 않는 특성을 불변(Immutable), 할당된 공간이 변하는 특성을 가변(mutable)라고 한다.
- mutable : 문자열 변경시 새로운 메모리 변경 필요 없이 변경 가능
String
String str = "hello";
str = str + " world";
- “hello” 값이 들어가있던 String 클래스의 참조 변수 str이 “hello world”라는 값을 가지고 있는 새로운 메모리영역을 가리키게 변경된다.
- 처음 선언했던 “hello”로 값이 할당되어 있던 메모리 영역은 Garbage로 남아있다가 GC(Garbage Collection)에 의해 사라진다.
- String 클래스는 불변하기 때문에 문자열을 수정하는 시점에 새로운 String 인스턴스가 생성된 것이다.
- 이와 같이 문자열 추가, 수정, 삭제 등의 연산이 빈번하게 발생하는 알고리즘에 String을 사용하면 Heap 메모리에 많은 임시 가비지가 생성되어 힙메모리가 부족으로 애플리케이션 성능에 치명적인 영향을 끼칠게 된다.
- 따라서 문자열의 추가,수정,삭제가 빈번하게 발생할 경우라면 String 클래스가 아StringBuffer/StringBuilder를 사용해야한다.
StringBuilder, StringBuffer
예제 코드
void stringClassTest() {
String str = "hello";
StringBuilder sbd = new StringBuilder();
StringBuffer sbf = new StringBuffer();
sbd.append("hello");
sbf.append("hello");
System.out.println("String 객체의 주소 = " + str.hashCode());
System.out.println("StringBuilder 객체의 주소 = " + sbd.hashCode());
System.out.println("StringBuffer 객체의 주소 = " + sbf.hashCode());
str += "world";
sbd.append("world");
sbf.append("world");
System.out.println("String 객체의 주소 = " + str.hashCode());
System.out.println("StringBuilder 객체의 주소 = " + sbd.hashCode());
System.out.println("StringBuffer 객체의 주소 = " + sbf.hashCode());
}
실행 결과
- String, StringBuilder, StringBuffer 타입의 변수를 선언하고 문자열 수정 전과 수정 후의
hashcode()
반환값을 출력해보았다.
hashcode()
: 각 객체의 주소값을 변환하여 생성한 객체의 고유한 정수값을 반환한다. 두 객체가 동일 객체인지 비교할 때 사용한다.
StringBuilder vs StringBuffer
공통점
- 공통점은 StringBuilder와 StringBuffer클래스의 공통점은 둘다 mutable한 성질을 가지고 있다는 것이다.
- 위의 이미지처럼 두 클래스 모두
AbstractStringBuilder
라는 추상 클래스를 상속받아 구현되어 있기 때문에 mutable한 성질을 가지고 있다.
AbstractStringBuilder
추상 클래스의 멤버 변수엔 다음 2가지 변수가 존재한다.
- value : 문자열의 값을 저장하는 byte 배열
- count : 현재 문자열 크기의 값을 가지는 int형 변수
append()
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
- StringBuilder와 StringBuffer 클래스의 문자열을 수정하고 싶으면
append()
메서드를 사용한다.
- StringBuilder , StringBuffer에 문자열을 추가하게 되면 추가할 문자열의 크기(길이)만큼 현재의 문자열을 저장하는 배열의 공간을 늘려주고, 늘려준 공간에 추가할 문자열을 넣어주는 방식으로 되어있다.
- 위에서 살펴본 내부동작을 통해 값이 변경되더라도 같은 주소공간을 참조하게 되는 것이며, 값이 변경되는 가변성을 띄게 되는 것입니다.
차이점
- 차이점은 동기화(Synchronization)이다.
- StringBuilder는 동기화를 지원하지 않는 반면, StringBuffer는 동기화를 지원하여 멀티 스레드 환경에서도 동작할 수 있다.
- 그 이유는 StringBuffer는 메서드에서
synchronized
키워드를 사용한다.
- StringBuilder는 싱글 스레드 환경에 적합하고 동기화를 고려하지 않아도 되는 스레드가 안전한 프로그램 개발시 사용한다.
- StringBuffer은 멀티 스레드 환경에 적합하고 연산이 많을 경우 유리하다.
- 추가로 StringBuilder은 동기화를 지원하지 않는 반면, 속도면에서는 StringBuffer보다 성능이 좋다.
Java에서의 synchronized
키워드
- Java에서
synchronized
키워드는 여러 개의 스레드가 한 개의 자원에 접근할려고 할 때, 현재 데이터를 사용하고 있는 스레드를 제외하고 나머지 스레드들이 데이터에 접근할 수 없도록 막는 역할을 수행한다.
- ex) 멀티스레드 환경에서 A 스레드와 B 스레드가 모두 같은 StringBuffer 클래스 객체 sb의 append() 메서드를 사용하려고 하면, 다음과 같은 절차를 수행한다.
- A 스레드 : sb의 append() 동기화 블록에 접근 및 실행
- B 스레드 : A 스레드 sb의 append() 동기화 블록에 들어가지 못하고 block 상태가 된다.
- A 스레드 : sb의 append() 동기화 블록에서 탈출
- B 스레드 : block에서 running 상태가 되며 sb의 append() 동기화 블록에 접근 및 실행
String, StringBuilder, StringBuffer 사용
String을 사용하는 경우
StringBuilder을 사용하는 경우
- 문자열 연산이 많은 경우
- 싱글 스레드 환경
- 동기화를 고려하지 않아도 되는 스레드가 안전한 프로그램 개발시 사용
StringBuffer을 사용하는 경우
참조
https://ifuwanna.tistory.com/221
https://velog.io/@heoseungyeon/StringBuilder와-StringBuffer는-무슨-차이가-있는가