String, String Builder, String Buffer

김성민·2023년 10월 22일

java 에서 문자열을 다루를 대표적인 클래스로 String , StringBuffer, StringBuilder 가 있다.

String

자바의 String은 참조형 타입 변수로 분류된다.
변수 선언 방식에는 대표적으로 2가지
1. 리터럴을 이용한 방식 ex) String str = "hello";
2. new 연산자를 이용한 방식 ex) String str = new String("hello");

리터럴을 이용한 방식으로 선언시 자바 메모리 힙영역의 String Constant Pool에 값이 저장
생성자를 통한 방식은 일반적인 참조형 타입처럼 메모리 힙 영역에 값이 저장

String vs String builder, String buffer

String과 StringBuffer/StringBuilder 클래스의 가장 큰 차이점은
String은 불변(immutable)의 속성을 갖는다는 점이다.

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    private final byte[] value;

String 객체 내부 구성 요소를 보면 value 값이 final(상수)형으로 정의되어 값을 바꾸지 못한다

    String str = "hello"; // "hello"
    int m1 = str.hashCode(); 
    // hasCode() : 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴
    str += " world"; // "hello world"
    int m2 = str.hashCode();
    System.out.println(m1 == m2); // false

기존에 "hello" 값이 들어가있던 문자열타입의 변수str이 "hello world"라는 값을 가지고 있는 새로운 메모리영역(m2)을 가리키게 변경된다. String 클래스는 불변하므로 문자열을 수정하는 시점에 새로운 String 인스턴스가 생성된다.

문자열 추가, 수정, 삭제 등의 연산이 자주 필요한 경우 String 클래스를 사용하면 힙 메모리에 많은 가비지가 생성되어 힙 메모리 부족(OutOfMemory)을 발생 시킬 수 있다.
따라서 변하지 않는 문자열을 자주 읽어들이는 경우에는 String 클래스를 사용하고 문자열 추가,수정,삭제 등의 연산이 자주 일어날 경우에는 아래의 Stirng Buffer, String Builder 클래스를 사용하는게 효과적이다.

(+연산을 사용시 컴파일 내부적으로는 StringBuilder 클래스를 사용한다...?)

2. String Buffer, String Builder

StringBuffer나 StringBuilder는 가변(mutable)적이다.

두 클래스는 내부 Buffer(데이터를 임시로 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 할 수 있도록 설계되어 있다.

String 객체는 한번 생성되면 불변적인 특징 때문에 값을 업데이트하면, 매 연산 시마다 새로운 문자열을 가진 String 인스턴스가 생성되어 메모리공간을 차지하게 되지만,

StringBuffer / StringBuilder 는 가변성 가지기 때문에 .append() .delete() 등의 메서드를 이용하여 동일 객체내에서 문자열 크기를 변경하는 것이 가능하다.

public final class StringBuffer
    extends AbstractStringBuilder
    implements Serializable, Comparable<StringBuffer>, CharSequence
{

    private transient String toStringCache; // final로 정의되지 않음

StringBuffer 내부 구조

    StringBuffer sf = new StringBuffer("hello"); // "hello"
    int m3 = sf.hashCode();
    sf.append(" world"); // "hello world"
    int m4 = sf.hashCode();
    System.out.println(m3 == m4); // true

3. StringBuffer vs StringBuilder

둘의 차이점은 멀티 쓰레드(Thread)에서 안전한지 유무이다.
StringBuffer : 동기화를 지원해서 멀티 쓰레드 환경에서 안전
StringBuilder : 동기화를 지원하지 않으며 멀티 쓰레드 환경에서 안전하지 않음

public class Stringtest extends Thread {

    StringBuffer strbf = new StringBuffer();
    StringBuilder strbd = new StringBuilder();

    new Thread(() -> {
      for (int i = 1; i <= 5000; i++) {
        strbf.append(0);
        strbd.append(0);
      }
    })
      .start();

    new Thread(() -> {
      for (int i = 1; i <= 5000; i++) {
        strbf.append(0);
        strbd.append(0);
      }
    })
      .start();

    new Thread(() -> {
      try {
        Thread.sleep(3000);
        System.out.println("StringBuffer.length : " + strbf.length());
        System.out.println("StringBuilder.length : " + strbd.length());
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    })
      .start();
  }
}

결과

StringBuffer.length : 10000
StringBuilder.length : 8772

정리

String 을 사용해야 할 때 :

String은 불변성
문자열 연산이 적고 변하지 않는 문자열을 자주 사용할 경우
멀티쓰레드 환경일 경우 

StringBuilder 를 사용 해야 할 때 :

StringBuilder는 가변성
문자열의 추가, 수정, 삭제 등이 빈번히 발생하는 경우
동기화를 지원하지 않아, 단일 쓰레드이거나 동기화를 고려하지 않아도 되는 경우
속도면에선 StringBuffer 보다 성능이 좋다.

StringBuffer 를 사용해야 할 때 :

StringBuffer는 가변성
문자열의 추가, 수정, 삭제 등이 빈번히 발생하는 경우
동기화를 지원하여, 멀티 스레드 환경에서도 안전하게 동작

참고사이트

https://inpa.tistory.com/entry/JAVA-%E2%98%95-String-StringBuffer-StringBuilder-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90#stringbuffer_vs_stringbuilder_%EC%B0%A8%EC%9D%B4%EC%A0%90
https://ifuwanna.tistory.com/221
https://marrrang.tistory.com/53

0개의 댓글