ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] String 덧셈 연산 대신 StringBuilder 또는 StringBuffer 객체를 활용해 성능 개선하기
    Today I Learned 2023. 10. 23. 23:04

    StringBuilder

    Java 5에서 추가된 문자열 생성이나 연산, 수정을 효율적으로 처리하기 위해 도입된 클래스이다. 자세한 메서드 호출 방법은 StringBuilder.java 내 설명을 참조할 수 있다.

    String text = new StringBuilder()
        // StringBuilder 객체에 주어진 문자열을 추가한다.
        .append("hello")
        .append("World!")
        // StringBuilder 객체 내 문자열의 지정한 인덱스에 주어진 문자열을 추가한다.
        .insert(5, ", ")
        // StringBuilder 객체 내 문자열의 지정한 인덱스 범위만큼을 주어진 문자열로 대치한다.
        .replace(0, 1, "H")
        .replace(7, 12, "world")
        // StringBuilder에 포함된 문자열을 String으로 변환한다.
        .toString();
        
    System.out.println(text);
    // Hello, world!

     

     

    반복적인 String 덧셈 연산 시 발생할 수 있는 문제

    String 객체는 Immutable한 불변 객체임을 주목할 필요가 있다. 한 번 생성된 String 객체는 불변 객체이기 때문에 수정할 수 없으므로, JVM은 String 덧셈 연산 시 Heap의 String 풀에 대상 String 객체가 존재하는지 검사해 존재하는 경우 해당 객체의 참조를 사용하고, 존재하지 않을 경우 새로운 String 객체를 생성해 Heap에 저장한다.

     

    예를 들어 다음의 String 연산을 보자.

    String text = "I" + " " + "have" + " " + "a" + " " + "pen.";

     

    위의 String 덧셈 연산 수행 과정에서 JVM은 각 String 객체들 간의 연산을 앞에서부터 수행하면서 중간 연산 결과 String 객체들을 모두 String 풀에 저장해야 한다. 따라서 위의 String 연산을 수행하기 위해 String 풀에 저장되는 String 객체들은 다음과 같다.

     

    "I"
    " "
    "have"
    "a"
    "pen."
    "I "
    "I have"
    "I have "
    "I have a"
    "I have a "
    "I have a pen."

     

     

    StringBuilder는 내부적으로 가변 문자열을 저장할 수 있는 공간을 할당받는다. 해당 공간의 크기는 기본값을 사용하거나 직접 공간의 크기를 생성자로 전달할 수 있는데, 메서드 호출을 통한 문자열 연산 과정에서 공간을 초과할 경우 해당 공간의 크기에 맞게 재생성된다. 모든 연산을 마친 뒤, toString() 메서드를 호출해 최종적으로 생성된 문자열을 반환한다. 이를 바탕으로 StringBuilder 객체를 통해 다음과 같이 결과 String을 생성하는 경우, String 풀에는 다음의 문자열 하나만이 저장된다.

     

    String text = new StringBuilder()
        .append("I")
        .append(" ")
        .append("have")
        .append(" ")
        .append("a")
        .append(" ")
        .append("pen.")
        .toString();
    "I have a pen."

     

    다음 코딩 테스트 문제 풀이에서 성능 차이를 확인할 수 있다. PersonalityTypes.java 소스코드에서 다음 부분에만 차이를 두고 정확성 및 성능 테스트를 진행한 결과는 다음과 같다. 왼쪽부터 순서대로 String 덧셈 연산, StringBuilder 객체 활용 연산의 수행 결과이다.

    private String determinePersonalityType(Map<Character, Integer> typeToScores) {
        // ...
        
        // String 덧셈 연산
        return "" + firstType + secondType + thirdType + fourthType;
        
        // StringBuilder 객체 활용 연산
        return new StringBuilder()
            .append(firstType)
            .append(secondType)
            .append(thirdType)
            .append(fourthType)
            .toString();
    }

     

    StringBuffer

    StringBuffer는 생성자 및 메서드 호출은 StringBuilder와 같은 방식으로 사용할 수 있지만, 내부적으로 동기화 동작이 포함되어 thread-safe하다는 특징이 있다. 레퍼런스 소스코드 상에서는 multi-thread 환경이 아닌 경우에는 동기화 동작이 존재하지 않는 StringBuilder가 일반적으로 더 나은 성능을 보이므로 StringBuilder를 사용하고, multi-thread 환경에서는 StringBuffer를 사용할 것을 권장하고 있다.

     

     

    Reference

    - java.lang.StringBuilder

    - java.lang.StringBuffer

    - StringBuilder vs StringBuffer in Java

    - ChatGPT

     

     

     

     

    댓글

Designed by Tistory.