👀 문자열 클래스 ( String / StringBuffer / StringBuilder )👀
1.String : final형으로서 각 char를 수정하거나, 접근할 수 없음.
2.StringBuffer : thread-safe 하고, 수정가능한 char 배열. Multi-thread에 사용.
3.StringBuilder : thread-safe 하지 않고, 수정 가능한 char 배열. Single-thread에 사용
Java에는 문자열을 다루는 방식이 굉장히 많습니다. String이 가장 흔하게 사용되는 방식이고, StringBuffer와 StringBuilder 또한 문자열을 다룰 수 있는 클래스입니다. 이 세 가지 클래스는 아래와 같은 차이점을 가지고 있습니다.
-
String vs StringBuffer, StringBuilder : 중간 값을 바꿀 수 있는가?
-
StringBuffer vs StringBuilder : thread-safe 한가? (동기적인가? or 멀티쓰레드를 관통하여 사용할 수 있는가?)
정리하면,
1. String : 상수(한번 선언되면 값이 변할 수 없음)이다. |
2. StringBuffer: 상수가 아니고, 동기적(멀티 스레드에서 손실이 없음)이다. |
3. StringBulider: 상수가 아니고, 비동기적이다. |
StringBuffer와 StringBuilder의 차이점은 멀티스레딩의 문제니 미뤄두고,
어찌됐든 String은 한번 선언되면 내부 char 값을 바꿀 수 없다는 결론을 볼 수 있습니다. 그럼 이게 무슨 의미가 있을까요? 왜 귀찮게 StringBuffer나 StringBuilder를 사용해야 할까요?
이 답은 "문자열을 합치는 것"에서 발생합니다.
String은 값을 바꿀 수 없습니다. 만약 두 문자열(A,B)을 하나의 문자열로 합친다면, [합쳐질 String 2개(A,B), 최종 String 1개(C)] = 총 3개의 String 클래스가 필요하다는 뜻입니다. 값을 바꿀 수 없어서 두 문자열(A,B)을 이어 붙일수는 없으니, 두 문자열의 크기를 갖는 새로운 String 클래스(C)를 생성해서 두 문자열(A,B) 의 값을 새 String에 넣어줘야 하는 것입니다. 따라서 수백, 수천개의 문자열을 하나로 합쳐야 되는 경우가 올 경우, 메모리의 스택 또는 힙에 몇 개의 String 클래스가 들어갈지 모르는 일이고, 이를 통해 메모리 관리적 측면에서 String은 좋은 방법이 아님을 생각할 수 있습니다.
반면 StringBuffer와 StringBuilder는 String을 위한 Buffer입니다. 두 문자열을 합친다면, 하나의 Buffer 클래스에 나머지 하나의 String을 더하는 방식으로 메모리가 쌓일 것이므로 메모리에는 하나의 클래스만 존재하면 됩니다. String에서는 불가능했던 일종의 '이어붙이기'가 Buffer에서는 가능한 것입니다. 따라서, 수백개의 문자열을 합친다고 한들 하나의 Buffer 클래스에 대하여 길이를 늘려가며 이어붙이기만 하면 되므로, 메모리 관리적 측면에서 더 좋은 방법이 되겠습니다.
StringBuffer와 StringBuilder 간의 차이점에 대해서 설명해보자면, 위에 설명했듯이 StringBuffer는 thread-safe한 특성을 가지고 있고, String Builder는 그러지 못합니다. 이게 무슨 뜻인지는 다음 코드를 보면 이해가 쉽게 될 것입니다.
StringBuffer stringBuffer = new StringBuffer(); // StringBuffer 객체
StringBuilder stringBuilder = new StringBuilder(); // StringBuilder 객체
// 1번 쓰레드 : 0~10000 넣기!
new Thread(() -> {
for(int i=0; i<10000; i++) {
stringBuffer.append(i+" ");
stringBuilder.append(i+" ");
}
}).start();
// 2번 쓰레드 : 0~10000 넣기!
new Thread(() -> {
for(int i=0; i<10000; i++) {
stringBuffer.append(i+" ");
stringBuilder.append(i+" ");
}
}).start();
// 결과확인을 위한 쓰레드
new Thread(() -> {
try {
Thread.sleep(10000);
// 반복문을 통해 비교하다가 다른 부분이 나타나면 해당 인덱스에서 (+-30)만큼의 문자열 출력
for(int i = 0; i<10000; i++){
if(stringBuffer.charAt(i) != stringBuilder.charAt(i)){
System.out.println("String Buffer: "+stringBuffer.substring(i-30, i+30));
System.out.println("String Buffer: "+stringBuilder.substring(i-30, i+30));
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
위 코드의 출력은 다음과 같습니다.
출력:
String Buffer: 71 0 572 1 573 2 574 3 575 4 576 5 577 6 578 7 579 580 581 8
String Builder: 71 0 572 1 573 2 574 3 575 4 5 6 577 6 578 7 579 580 8 581
여기서 볼수 있는 것이, StringBuffer는모든 값이 누락되지 않고 잘 들어간 반면 StringBuilder는"576"이 누락되어 사라졌습니다. 왜냐하면 StringBuffer는 여러 쓰레드로부터 오는 입력들을 밀리더라도 순차적으로 입력시켜주는 반면, StringBuilder는 해당 기능이 없어 쓰레드간 입력의 충돌에서 오는 손실을 막을 수 없기 때문입니다.
하지만, StringBuilder가 그만큼 속도가 더 빠르므로, Single thread로 구현되는 프로그램의 경우 StringBuffer를 쓰는 것보다 StringBuilder를 쓰는 것이 시간적 이득이 있을 수 있습니다.
<참고>
docs.oracle.com/javase/8/docs/api/
novemberde.github.io/2017/04/15/String_0.html
'Language > Java' 카테고리의 다른 글
[Java] ArrayList, LinkedList 차이 (0) | 2021.04.01 |
---|---|
[Java] BufferedWriter Int형 출력 / BufferedWriter 정수 출력 (0) | 2021.01.11 |
[Java] JAVA 절댓값 구하는 함수 Math.abs() (0) | 2021.01.04 |
[Java] Java 배열 깊은 복사 & 얕은 복사 / Deep Copy & Shallow Copy / Java 객체 배열 복사 (5) | 2021.01.04 |