본문 바로가기

Java

String / StringBuilder / StringBuffer

Java에서 문자열을 사용할 때 String만 주야장천 썼었는데 StringBuilder와 StringBuffer에 대해서도 정리해야겠다.

 

String은 불변 객체라 했었다. 물론 바꾸는 척 하는 방법은 얼마든지 있었다. + 연산과 concat() 메서드가 주로 쓰이지.

 

근데 문자열 연산에 주로 쓰이는게 StringBuilder와 StringBuffer이다. 

 

왜? String에서 + 연산으로 문자열을 합치면 기존게 변하는 게 아니라 새로운 인스턴스가 생겨서 메모리 낭비가 된다.

 

StringBuilder / StringBuffer

 

둘 다 가변 객체이다. String과 달리 동일한 객체 내에서 수정이 일어나 연산 속도가 매우 빠르다.

 

appen()를 이용해서 문자열을 합친다.

StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b");
System.out.println(sb); // ab

 

그 외의 메소드들 

String str = "abc";
StringBuffer sb = new StringBuffer(str); // String -> StringBuffer

sb.toString() // String으로 변환 
sb.substring(0,2); // ab
sb.insert(2, "d"); // abdc
sb.delete(2,3); // ab
sb.append("d") // abcd
sb.length() // 3
sb.reverse() // cba
sb.capacity() // 16

 

delete가 약간 헷갈릴 수 있는데 2번 인덱스부터 3-1 인덱스를 삭제한다. 이런 뜻이다.

 

그리고 capacity()는 용량 크기를 구하는 함수인데 16인 이유는 버퍼와 빌드 모두 기본적으로 16개의 초기 용량을 가진다.

 

어? 길이가 초과되면요? 내부적으로 자동으로 증가시킨다. 

 

참고로 문자열 비교할 때 toString()으로 변환해 준 뒤 equlas()로 비교해야 올바르게 나온다.

 

그럼 StringBuilder와 StringBuffer는 똑같은 거 같은데 뭐가 차이가 있냐 싶죠?

 

스레드에서의 안전성이 차이가 납니다.

 

StringBuffer는 synchronized로 구현되어 있어 스레드에서 안전하지만 StringBuilder는 synchronized로 구현되어 있지 않아서 쓰레드에 안전하지 않습니다. 

더보기

synchronized란? 여러 개의 쓰레드가 동시에 공유 자원에 접근할 때 발생할 수 있는 문제를 방지하기 위한 키워드.

무슨 소린가 싶죠.. 예제를 봐야해요.

import java.util.*;

public class Main extends Thread{
  public static void main(String[] args) {
    StringBuffer stringBuffer = new StringBuffer();
    StringBuilder stringBuilder = new StringBuilder();

	// 첫 번째 스레드
    new Thread(() -> {
        for(int i=0; i<10000; i++) {
            stringBuffer.append(1);
            stringBuilder.append(1);
        }
    }).start();

	// 두 번째 스레드
    new Thread(() -> {
        for(int i=0; i<10000; i++) {
            stringBuffer.append(1);
            stringBuilder.append(1);
        }
    }).start();

    new Thread(() -> {
        try {
            Thread.sleep(2000);

            System.out.println("StringBuffer.length: "+ stringBuffer.length()); // thread safe 함
            System.out.println("StringBuilder.length: "+ stringBuilder.length()); // thread unsafe 함
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
  }
}

 

결과 값이 둘 다 20000으로 나올 거 같잖아요? 아닙니다. buffer는 20000으로 나오지만 builder는 확신할 수 없어요. 19749 이렇게 나올 수 있다는 거죠. 왜 그럴까요? 

 

아까 Builder는 동기화 키워드가 안 붙어 있다고 했잖아요? 그래서 그렇습니다. 동기화가 안된다는 거는 동시에 한 자원에 접근했을 때 데이터 손실이 발생할 수 있다는 거예요. 

 

예를 들어 첫 번째 스레드가 2번째에 1을 추가했는데 두 번째 스레드도 동시에 2번째에 1을 추가하면 한 스레드의 값이 덮어씌워지는 거죠. 그래서 데이터 손실이 발생합니다. 

 

* append는 문자열 연결 아닌가요..? 맞습니다. 근데 append는 int가 들어오면 String으로 바꿔서 넣어줘요~ 


참고 출처

☕ 자바 String / StringBuffer / StringBuilder 차이점 & 성능 비교 (tistory.com)

 

☕ 자바 String / StringBuffer / StringBuilder 차이점 & 성능 비교

자바에서는 대표적으로 문자열을 다루는 자료형 클래스로 String, StringBuffer, StringBuilder 라는 3가지 자료형을 지원한다. 위 3가지 클래스 자료형은 모두 문자열을 다루는데 있어 공통적으로 사용되

inpa.tistory.com

 

'Java' 카테고리의 다른 글

익명 클래스 / 람다식 / 메소드 참조  (1) 2024.06.08
형 변환 / valueOf() / stream  (0) 2024.06.07
static과 final  (1) 2024.06.05
Class, Object, Instance  (0) 2024.06.05
Java 메모리 - Stack, Heap, Method(Static)  (0) 2024.06.03