본문 바로가기

자바봄/리뷰

2. String + 연산대신 StringBuilder를 사용하자

String은 보면 immutable(불변)의 특징이 있습니다. 

String a = "a";				// 1
String a = new String("a");		// 2

1번 코드처럼 리터럴하게 a변수에 스트링을 할당하는 것은, 결국 새로운 스트링 객체 하나를 선언하는 것과 똑같습니다.

    @Test
    @DisplayName("a의 주소값이 같을까?")
    void hashCodePrint() {
        String a = "a";
        String b = "b";

        System.out.printf("String a : hashcode = %d\n", a.hashCode());
        System.out.printf("String b : hashcode = %d\n", b.hashCode());

        a = a + b;
        System.out.printf("String a : hashcode = %d\n", a.hashCode());
        System.out.printf("String b : hashcode = %d\n", b.hashCode());
    }

이 결과를 보면 아래와 같이 String a가 기존과는 다른 곳에 저장이 됨을 알 수 있습니다.

String a : hashcode = 97
String b : hashcode = 98
String a : hashcode = 3105
String b : hashcode = 98

따라서 + 연산을 할 때 그때마다 새로운 String 객체를 생성하고, 그에 따른 비용이 든다는걸 알 수 있습니다.

 

+ 연산대신 StringBuilder를 사용할 때 장점을 보여드리겠습니다.

먼저 StringBuilder는 AbstractStringBuilder라는 클래스를 상속받아 만들어졌는데, 이 안을 살펴보면 필드변수가 모두 가변입니다. 그리고 StringBuilder에서 append(String str) 할 때의 로직을 보면 필드변수인 value에 str.getChars라는 메서드를 이용해서 문자열의 값을 수정해줍니다.

이후에는 toString() 메서드를 이용해 가변이었던 value대신 새로운 String 객체를 생성합니다. 

+ 연산으로 String 객체를 생성하는 비용보다, StringBuilder안의 append() 메소드를 이용한 연산 비용이 더 싸기 때문에, 그만큼 String을 이어주는 연산에 걸리는 시간이 덜 들어가게 됩니다.

    @Test
    @DisplayName("stringBuilder의 string 생성시간이 더 작다")
    void stringBuilder() {

        long stringAddStart = System.nanoTime();
        String test1 = "";
        for(int i =0; i<100 ; i++){
            test1 += "javabom";
        }
        long stringAddTotalTime = System.nanoTime() - stringAddStart;

        long stringBuilderStart = System.nanoTime();
        StringBuilder test2 = new StringBuilder();
        for(int i =0; i<100 ; i++){
            test2.append("javabom");
        }
        long stringBuilderTotalTime = System.nanoTime() - stringBuilderStart;

        System.out.printf("string + operation time %d\n", stringAddTotalTime);
        System.out.printf("stringBuilder operation time %d\n", stringBuilderTotalTime);

        assertThat(test1).isEqualTo(test2.toString());
        assertThat(stringAddTotalTime > stringBuilderTotalTime).isTrue();
    }

이 테스트코드는 통과하며, System.out.printf로 찍은 결과 역시 현저하게 차이납니다.

string + operation time 158123
stringBuilder operation time 4627