카테고리 없음

[Kotlin] 코딩 테스트 성능 최적화: joinToString과 StringBuilder의 선택 기준

yujinius 2024. 12. 26. 19:43

코딩 테스트에서 문자열을 효율적으로 처리하기 위해서는 joinToString과 StringBuilder의 차이를 이해하고 적절히 활용하는 것이 중요하다

joinToString 함수의 내부 구현

Kotlin의 joinToString 함수는 컬렉션의 요소를 문자열로 변환하여 연결하는 데 사용된다. 이 함수는 내부적으로 StringBuilder를 활용하여 문자열을 생성하며, 다음과 같이 정의되어 있다:

https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/join-to-string.html

https://github.com/JetBrains/kotlin/blob/rrr/2.1.0/core-docs/libraries/stdlib/common/src/generated/_Collections.kt#L3509

public fun <T> Iterable<T>.joinToString(
    separator: CharSequence = ", ",
    prefix: CharSequence = "",
    postfix: CharSequence = "",
    limit: Int = -1,
    truncated: CharSequence = "...",
    transform: ((T) -> CharSequence)? = null
): String {
    return joinTo(
        StringBuilder(), separator, prefix, postfix, limit, truncated, transform
    ).toString()
}

이 정의에서 알 수 있듯이, joinToString은 StringBuilder를 사용하여 문자열을 조합한 후, 최종적으로 toString()을 호출하여 결과 문자열을 반환한다.

StringBuilder의 동작 원리

StringBuilder는 가변 문자열을 효율적으로 처리하기 위한 클래스이다. 내부적으로 버퍼를 사용하여 문자열을 조합하며, 새로운 문자열을 추가할 때마다 새로운 객체를 생성하지 않고 기존 버퍼를 활용하여 성능을 최적화한다. 따라서, 반복적인 문자열 연결 작업에서 StringBuilder를 사용하면 메모리 사용량과 실행 시간을 줄일 수 있다.

성능 비교: joinToString vs StringBuilder

joinToString은 다양한 기능을 제공하지만, 단순히 문자열을 연결하는 작업에서는 불필요한 오버헤드가 발생할 수 있다. 반면, StringBuilder는 이러한 부가 기능 없이 순수하게 문자열을 연결하므로, 성능 면에서 유리하다.

프로그래머스 기초 코딩테스트 문제인 문자 리스트를 문자열로 변환하기 에서 이를 비교해봤다.

 

joinToString

class Solution {
    fun solution(arr: Array<String>): String {
        return arr.joinToString("")
    }
}

 

StringBuilder 가 훨씬 빠르다

class Solution {
    fun solution(arr: Array<String>): String {
        val result = StringBuilder() // StringBuilder 생성
        for (char in arr) {          // 배열의 각 원소 순회
            result.append(char)      // StringBuilder에 추가
        }
        return result.toString()     // 최종 문자열 반환
    }
}

 

위와 같은 경우, StringBuilder를 직접 사용하는 것이 joinToString을 사용하는 것보다 더 효율적이다.


joinToString과 StringBuilder 의 각 장점은 다음과 같다

joinToString의 장점

  1. 중간에 구분자(separator) 추가:
    • joinToString은 요소 사이에 특정 구분자를 쉽게 삽입 가능
    • 예: arr.joinToString(", ") → "a, b, c"
  2. 접두사와 접미사(prefix, postfix) 추가:
    • 문자열 앞뒤에 추가적인 내용을 간단히 추가 가능
    • 예: arr.joinToString(", ", prefix = "[", postfix = "]") → "[a, b, c]"
  3. 변환(transform) 기능:
    • 각 요소를 변환하여 처리 가능
    • 예: arr.joinToString("") { it.uppercase() } → "ABC"
  4. 간결한 코드:
    • 코드가 간단하고 읽기 쉬움

StringBuilder의 장점

  1. 불필요한 기능 제거로 성능 최적화:
    • StringBuilder는 단순히 문자열을 이어 붙이는 데 최적화되어 있으므로 불필요한 오버헤드가 없다.
  2. 단순 문자열 연결에 효율적:
    • 구분자, 접두사, 접미사 등의 추가 작업이 필요 없을 때 더 빠르다.
  3. 메모리 관리:
    • 내부적으로 버퍼를 사용하므로, 대규모 문자열 생성 시 더 효율적이다.
    •  

정리

  • joinToString을 사용하는 경우:
    • 요소 간의 구분자를 추가하거나 변환이 필요한 경우.
    • 접두사와 접미사를 추가해야 하는 경우.
    • 코드의 간결성과 가독성이 중요한 경우.
  • StringBuilder를 사용하는 경우:
    • 단순히 문자열을 이어 붙이는 작업만 필요하고, 성능 최적화가 중요한 경우.
    • 대규모 데이터(예: 배열 크기가 매우 클 때)에서 더 높은 효율을 원할 때.
    •  

결론

코딩 테스트에서 성능 최적화가 중요한 상황에서는 StringBuilder를 직접 사용하는 것이 좋다. 특히, 단순한 문자열 연결 작업에서는 StringBuilder가 더 나은 성능을 제공한다. 그러나 코드의 가독성과 유지 보수성을 고려해야 하는 경우에는 joinToString을 사용하는 것이 더 적합할 수 있다.

따라서, 상황에 맞게 두 가지 방법을 적절히 활용하는 것이 중요하다.

하지만 코딩 테스트의 대부분의 상황에서는 StringBuilder를 직접 사용하는 것이 시간 복잡도 최적화에 좋다!