본문 바로가기
Java/The Java

[Java] StringBuffer vs StringBuilder 차이 (무엇을 써야할까?)

by 어썸오184 2021. 8. 2.
728x90
반응형

String in Java

자바에서 String을 다룰 때 알아두어야 할 것이 있습니다.

  1. String은 참조 자료형이다. 근데 자바에서 좀 특별하게 취급된다.
  2. 불변(Immutable)이다.

String pool

자바에서 String은 일반 객체와 달리 heap 영역의 String pool에서 관리됩니다. 문자열은 다음과 같이 두 가지 방법으로 생성할 수 있습니다.

public static void main(String[] args) {
    String a = "Hello";
    String b = new String("Hello");
}

a, b 두 개의 문자열은 JVM 내에서 생성되는 영역이 다릅니다.

 


출처: https://www.journaldev.com/797/what-is-java-string-pool

따라서 String a = “Hello”; 로 문자열을 생성하면 변수 a는 String pool 내의 영역을 가리키고

String a = new String(“Hello”); 로 문자열을 생성하면 새로 생성된 객체의 주소를 가리키게 됩니다. 그래서 == 비교를 통해 주소값을 비교하면 다음과 같은 결과가 나타납니다.

public static void main(String[] args) {
    String a = "Hello";
    String b = "Hello";
    String s1 = new String("Hello");
    String s2 = new String("Hello");

    System.out.println(a == b);    // true
    System.out.println(s1 == s2);  // false
}

a와 b는 힙 영역의 동일한 주소 공간을 가리키고, s1과 s2는 각각 다른 주소 공간을 가리키고 있기 때문이죠.

Immutable

자바의 String은 불변 객체이기 때문에 다음과 같이 문자열에 연산을 하면 새로운 객체를 생성하게 됩니다.

public static void main(String[] args) {
    String a = "Hello";
    a += " World!";
}

 

매번 연산을 할 때마다 새로운 String을 생성하기 때문에 String 자체에 연산을 하는 것은 매우 비효율적입니다. 예를 들어 아래의 코드는 총 10번의 문자열 생성을 하게됩니다.

public static void main(String[] args) {
    String a = "";
    for (int i = 0; i < 10; i ++) {
        a += i;
    }
}

그래서 자바에서는 이런 비효율을 해결하기 위해 StringBufferStringBuilder라는 객체를 제공합니다. 추가적인 연산이 일어날 때마다 객체 연산 결과들을 저장해놓고 있다가 toString()을 호출하는 순간에 딱 한번만 String을 생성합니다.

public static void main(String[] args) {
    StringBuffer sb = new StringBuffer();
    sb.append("Hello");
    sb.append(" World");
    String a = sb.toString();
}

 

StringBuffer vs StringBuilder

StringBuffer와 StringBuilder는 기본적으로 같은 기능을 제공합니다. 둘의 차이는 스레드 안정성에 있습니다.

  1. StringBuffer는 Thread-safe하지만 느리다.
  2. StringBuilder는 Thread-safe하지 않지만 빠르다.

그래서 싱글 스레드로 처리를 하거나 race condition을 신경쓰지 않아도 되는 경우에는 StringBuilder를 사용하는 것이 더 효율적입니다.

StringBuilder 대신 StringBuffer를 사용하는 경우를 생각해보면 다음과 같은 상황을 고려해볼 수 있겠네요.

public class Service {
    StringBuffer sb = new StringBuffer();    // 공유변수 sb

    public String doSomething() {
        sb.append(something);  // 공유변수 write
    }
}

만약 Service 객체가 여러 쓰레드에 의해 공유된다면 StringBuffer를 사용해야겠네요. 하지만 꼭 공유변수를 둬야하는 상황이 아니라면 아래처럼 애초에 멀티 쓰레드 환경에서 공유변수를 사용할 일이 없도록 설계를 해주는게 더 좋을 것 같습니다.

public class Service {

    public String doSomething() {
        StringBuilder sb = new StringBuilder();
        sb.append(something);
    }
}
728x90
반응형

댓글