본문 바로가기
Java

String,StringBuilder,StringBuffer

by 스르나 2021. 3. 10.

자바에서 문자열 처리는 주로 String과 StringBuilder를 이용한다.

 

이번에는 2가지를 각각 알아보고, 비교를 해본다.

 

String

우선 String이다. String은 자바에서 문자열을 다룰때 가장 기본적인 형태이다.

String의 생성방식에는 literal,new 2가지 방식이 있다.

String str="string!!";

String str2=new String("string !!");

2방식 모두 heap에 저장이된다. 그렇지만 차이점이 있는데 literal같은 경우 string constant pool이라는 공간에 들어간다. string constant pool에 해당 literal을 넣고 String 변수가 가르키고 있는 형태인것이다. 이렇게 string constant poll에 저장하는 이유는 문자열은 같은 문자열을 다루는 경우가 많은데 이 문자열들을 일일이 heap에 생성하는 것은 메모리 낭비이니 string constant pool에 저장하고 가르키는 형태로 하는 것이다.여기서 한가지 더 알수있는것은 String str="string !!"를 str="hello"로 변경하면 기존의 "string !!"는 string constant pool에있다 가비지 컬렉터에 의해 제거 되는 것이다.

반대로 new로 생성한 String은 일반적인 인스턴스와 같은 공간에 들어간다고 생각하면 된다.

 

다음으로 String에 2가지 생성방식에서 중요한 점은 2가지 생성방식으로 위의 코드처럼 같은 문자열을 다룰때 equals메소드의 리턴은 false가 된다. true를 원한다면 intern메소드로 literal를 가져와야한다.

 

String str="string!!";

String str2=new String("string !!");

str.equals(str2); // false를 리턴한다.

str.equals(str2.intern()); //true를 리턴한다

 

다음으로 String의 중요한 점은 String은 immutable한 특성을 지닌다는 것이다.

 

위 그림에서 왼쪽은 틀린것이다. 기존의 문자열을 수정한다고 해서 string constatnt pool에 있던 literal이 변경되는 것이 아니다. 기존에 literal은 그대로 string constant pool에 존재하고 새로운 literal이 생성되는 것이다.

이런것은 문자열간의 +연산도 마찬가지다. 그렇기 때문에 String으로 연산을 자주하다 보면 가비지 컬렉터가 자주 발생하게 된다.

 

StringBuilder,StringBuffer

다음으로는 StringBuilder,StringBuffer이다. 일단 StringBuiler와 StringBuffer는 동기화 유무의 차이만 있다. StringBuffer는 동기화를 처리해준다. 반대로 StringBuilder는 동기화를 처리하지 않는다. 그렇기 때문에 StringBuffer는 성능이 다소 부족한 편이다. 동기화를 제외하고는 모두 같으니 설명은 StringBuiler로 하겠다.

 

그럼 이 StringBuilder는 String과 무슨 차이점이 있는지 알아보자.

StringBuiler는 String과 다르게 생성하는 방법이 new로 생성하는 방법 한가지 뿐이다.

StringBuiler builer=new StringBuiler("hello");

StringBuiler builer2=new StringBuiler(20);

생성자에는 초기 문자열을 넣을 수도 있고, 미리 문자열의 크기를 정할 수 도있다.

여기서 문자열의 크기를 정하는 방식이 무엇인지 좀 자세히 볼 필요가 있다.

 

 

위의 코드는 String의 원형이고 아래 코드는 StringBuiler의 추상 클래스이다. 2가지 모두 char[] value라는 문자 배열을 가지고 있다.

 

느낌이 오겠지만 자바에서는 문자열을 char[] value에 저장한다. 위에서 StringBuiler의 생성자에 숫자를 넣는 것은 char배열의 크기를 미리 정하는 것이다. 이렇게 미리 정하는 이유는 간단하다. 배열이기 때문에 미리 크기를 정하는 것이다. 미리 크기를 정하지 않고 문자열을 바로 넣었다가 배열의 크기가 넘어가면 새로운 배열을 만들어야 하기 때문에 성능에 저하가 오기때문이다.

 

그리고 아마 코드를 보고 알았겠지만 String은 final키워드를 사용하고 StringBuilber는 final키워드를 사용하지 않는다. 그렇기 때문에 StringBuiler는 mutable한 특성을 가진다. 즉, 수정이 가능하다.

애초에 StringBuiler는 문자열의 수정을 용이하게  하기 위해 만들어 졌기때문이다.

 

 

String,StringBuiler 사용시 주의점

그럼 String과 StringBuiler의 사용시 주의할 점을 알아보자.

 

1. null을 다룰때

우선 null을 다를때 이다.

public static void main(String[] args) {
        String str="asd";
        String nullStr=null;
        String str2="dsa";

        System.out.println(str+nullStr+str2); //asdnulldsa

        StringBuilder builder=new StringBuilder();
        builder.append(str);
        builder.append(nullStr);
        builder.append(str2);

        System.out.println(builder.toString()); //asdnulldsa

        ////////////////////
        str.concat(null);
        System.out.println(str); // 에러 발생!!!

    }

 

우선 String,StringBuiler 모두 null이 중간에 들어오면 null을 문자열로 처리한다. 하지만 String의 concat 연산은 null을 처리하지 못한다.

 

2. ==,equals

다음으로는 String과 StringBuiler의 ==,equals연산차이다.

 

public static void main(String[] args) {
        String str="hello world!";
        String str2="hello world!";
        String str3=new String("hello world!");

        System.out.println(str==str2); //true

        System.out.println(str.equals(str2)); //true

        System.out.println(str==str3); //false

        System.out.println(str==str3.intern()); //true

        System.out.println(str.equals(str3)); //true

    }

먼저 String은 내부적으로 equals연산을 오버라이딩해서 사용하기 때문에 literal과 new로 생성한 String에 대해서는 equals연산은 문자열 값에 대한 비교를 하기 때문에 정확한 값을 리턴해준다. 하지만 ==연산은 literal과 new 비교를 인스턴스가 같은 것인지 비교하기 때문에 다르다고 나온다.(str==str3는 false인것)

 

다음으로는 StringBuiler이다.

public static void main(String[] args) {
        StringBuilder builder=new StringBuilder("hello world!");
        StringBuilder builder2=new StringBuilder("hello world!");
        StringBuilder builder3=builder;
        String str="hello world!";

        System.out.println(builder.equals(builder2)); // false
        System.out.println(builder==builder2); //false

        System.out.println(builder==builder3); //true
        System.out.println(builder.equals(builder3)); //true

        System.out.println(str.equals(builder)); //false

    }

StringBuiler는 인스턴스를 생성하기 때문에 ==,equals연산 결과 false를 반환한다.

 

3. 더하기 연산

String과 StringBuiler의 가장 큰 차이점이자 String을 사용할때 주의할 점이다.

 

 public static void main(String[] args) {
        String str="a";

        for(int i=0;i<10;i++){
            str+="a";  // 컴파일시 new StringBuilder(str).append("a")로 바뀐다.
        }

        System.out.println(str);

        //////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////
        
        StringBuilder builder=new StringBuilder("a");

        for(int i=0;i<10;i++){
            builder.append("a");
        }

        System.out.println(builder.toString());
    }

for문 안에서 String의 더하기 연산은 컴파일시 StringBuiler를 새로 생성하기 때문에 성능에 저하가 올 수 있다. 그렇기 때문에 for문 안에서 더하기 연산은 StringBuiler를 사용하는 것이 좋다.

 

 

'Java' 카테고리의 다른 글

자바 Exception  (0) 2021.10.09
자바 Optional  (0) 2021.05.03
Collection Framework 정리표  (0) 2021.02.25
자바 synchronized 이해  (0) 2021.02.24
JVM  (0) 2021.02.14