String, StringBuffer, StringBuilder 중에 어떤 것을 사용해야 하나

String 클래스

String 변수를 초기화할 때 리터럴로 초기화된 문자열 객체는 스택 영역에 저장되고, 생성자를 통해 초기화하면 힙 영역에 저장된다.

String s1 = "java"; // 스택
String s2 = new String("java"); // 힙 영역

String 클래스의 객체는 생성된 이후 변경이 불가능한 immutable 클래스여서 문자열의 내용을 변경하는 메소드를 제공하지 않는다.

String 클래스는 기본 자료형처럼 사용할 수 있다.

String s1 = "java"; // 스택에 문자열 객체를 생성

리터럴로 다른 String형 변수를 사용하는 경우 하나의 리터럴이 공유된다.

리터럴을 사용하는 경우 리터럴을 사용하는 것이 효율적이다.

String s1 = "java";
String s2 = "java"; // s1과 동일한 문자열 객체를 가르킨다. => s1과 같은 메모리 주소!
String s3 = new String("java"); // 다른 메모리 주소를 가르킨다.

String 생성자

다음 링크에서 Constructors(생성자) 항목을 확인합니다.
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html

문자열 데이터의 비교는 compareTo(), equals() 메소드를 사용한다.

String s1 = "java";
String s2 = "java";
String s3 = new String("java");
System.out.println(s1.compareTo(s3)); // 0(다르면 0이 아닌 정수값을 반환)
System.out.println(s2==s2); // 같은 메모리 주소를 가르키고 있어 true를 반환
System.out.println(s3.equals(s1)); // true(다르면 false를 반환)
System.out.println(s3.==s1)); // 다른 메모리 주소를 가르키고 있어 false를 반환

어떤 자료형이든 문자열로 변환하기 위해서는 valueOf()를 사용한다.

valueOf() 메소드는 static 메소드이기 때문에 객체를 생성하기 않고 바로 사용이 가능하다.

System.out.println(String.valueOf(123));
int[] arr = new int[5];
System.out.println(String.valueOf(arr));

StringBuffer 클래스

String 클래스와 StringBuffer클래스 비교

Sting 클래스 StringBuffer 클래스
immutable(변경 불가) mutable(문자열 객체의 내용을 변경할 수 있다.)
  • StringBuffer는 내부적으로 버퍼(Buffer)를 가지고 있어 문자열 데이터를 이곳에 저장한다.

String 문자열 객체를 수정하려면 일반적으로 StringBuffer 클래스로 변환하여 수정 후 다시 String 문자열로 전환하는 방법을 사용한다.

생성자

StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer(5); // 길이 5인 버퍼를 갖는 객체를 생성
StringBuffer sb3 = new StringBuffer("java"); // test를 저장할 수 있는 버퍼를 같는 객체를 생성

메소드

StringBuffer sb = new StringBuffer();
sb.append("test"); // test문자열을 추가
System.out.println(sb.capacity()); // 버퍼 크기를 반환
System.out.println(sb.length()); // 버퍼에 저장된 문자열의 길이

append() 메소드는 boolean, double, float, int, long, String, StringBuffer형으로 오버로딩되어 정의되어 있다. (자바 API문서에서 찾아 봅시다.)

그외 메소드를 공식문서를 참고.

String, StringBuffer, StringBuilder 클래스의 비교

String, StringBuffer, StringBuilder 클래스들은 모두 java.lang 패키지에 포함되어 있어 import하지 않고 바로 사용이 가능하다.

처리속도 비교

String 클래스의 문제점

아래 <비교에 사용된 코드>에서 'String 클래스'를 먼저 살펴보겠습니다.
java는 String 자료형에 + 연산이 있을 때마다 새로운 String 객체를 만들어 냅니다.
비교를 위한 코드에서 for문에 1000번 반복되는 동안
한 번 실행할 때마다 기존의 문자열 객체는 그대로 두고 'java'가 추가된 새로운 문자열을 생성합니다.
이때 기본 문자열이 남아 있게 되고 글자 하나가 3byte라고 하면 사용되지 않는 문자열 12바이트가 메모리에 남아 있게 됩니다.
이를 1000번 반복하고 계속 누적해서 사용하지 않는 메모리가 쌓이게 되면 12byte+24byte+...998번의 크기가 되어 프로그램의 실행 속도가 점점 느려진다.

문자열을 빈번하게 변경하는 경우에는 String 클래스를 사용하지 않는다.

처리속도 비교 결과

** 처리속도 측정 방법**

  • System 클래스가 제공하는 nanoTime() 메소드를 사용
  • for문을 시작하기 전에 start에 시간을 저장하고 작업이 종료되면 end에 시간을 저장해 end시간과 start시간의 차를 측정하여 비교
  • nanoTime() 메소드는 현재 시간이 기준점부터 몇 초가 경과했는지 나타내 주는 값을 반환(단위 10-e9)

실행결과
String 클래스의 처리시간: 6.3172밀리세컨
StringBuffer 클래스의 처리시간: 0.3547밀리세컨
StringBuilder 클래스의 처리시간: 0.1514밀리세컨
실행 환경에 따라 차이가 있을 수 있지만 속도 순위는 같습니다.

결론

처리 시간은 String <<<<<<<<< StringBuffer < StringBuilder의 순이면
메모리에 대해서도 String 클래스는 기존 문자열을 그대로 두고 처리되는 반면 StringBuffer와 StringBuilder는 기존 공간에 필요한 부분만 할당받아 사용하기 때문에 메모리 낭비가 발생하지 않는다.

따라서, 바뀌지 않는 문자열은 String 클래스를 사용하고, 변경이 자주 발생하는 문자열을 다룰 때에는 StringBuffer 클래스를 사용하는 것이 더 효과적이다. Thread 환경에서와 같이 동시성 제어를 고려하지 않는 경우에는 StringBuilder 클래스를 사용하는 것이 더 효과적이다.

참고. StringBuffer는 Thread 환경을 고려한 메소드가 고려되었고, StringBuilder는 Thread 환경이 전혀 고려되지 않도록 메소드가 구현되어 속도의 차이가 발생한다.

<비교에 사용된 코드>

public class StringTest {

    public static void main(String[] args) {
        long start, end;
        final String text = "java";

        // String 클래스의 처리속도
        String str = new String();
        start = System.nanoTime();
        for(int i=0; i<1000; i++) str = str + text;
        end = System.nanoTime();
        System.out.println("String 클래스의 처리시간: "+(end-start)/100000.0+"밀리세컨");

        // StringBuffer 클래스의 처리속도
        StringBuffer sb = new StringBuffer();
        start = System.nanoTime();
        for(int i=0; i<1000; i++) sb = sb.append(text);
        end = System.nanoTime();
        System.out.println("StringBuffer 클래스의 처리시간: "+(end-start)/100000.0+"밀리세컨");

        // StringBuffer 클래스의 처리속도
        StringBuilder sbd = new StringBuilder();
        start = System.nanoTime();
        for(int i=0; i<1000; i++) sbd = sbd.append(text);
        end = System.nanoTime();
        System.out.println("StringBuilder 클래스의 처리시간: "+(end-start)/100000.0+"밀리세컨");
    }

}

+ Recent posts