[Kotlin] STL 정리하기 - String, StringBuilder


개요

최근 알고리즘 문제 풀이 언어를 C++Kotlin으로 전환하고 있습니다. 알고리즘 풀이 언어로 선택한 언어의 학습에 있어서 가장 중요한 것은 바로 Standard Library입니다. 해당 언어의 STL을 다루는 능력에 따라 문제 풀이의 속도와 효율성이 천차만별로 달라지게 됩니다. 특히나 코틀린의 경우, 다른 언어에 비해 자료구조나 문자열에 대해 훨씬 다양한 STL이 제공되기 때문에 이를 숙지하는 것이 굉장히 중요합니다. 따라서 유용한 Kotlin STL을 블로그에 싹 정리해보기로 하였습니다. 이번 글은 StringStringBuilder에 대한 내용입니다.


String vs StringBuilder

안드로이드 프로그래밍을 할 때에는 String만을 주로 사용합니다만, ps에 있어서는 StringBuilder가 필요한 순간이 많습니다. 두 객체의 차이는 다음과 같습니다.

String

  • immutable
  • 멀티스레드 환경에서 안전
  • 문자열이 수정될 때마다 새로운 객체가 Heap에 할당

StringBuilder

  • mutable
  • 멀티스레드 환경에서 안전하지 않음
  • 문자열 삽입, 삭제, 수정 시에도 기존 객체를 유지

즉, 하나의 문자열을 지속적으로 수정하여 답을 내는 경우에는 StringBuilder의 성능이 압도적으로 뛰어납니다. 특히 객체의 재할당 없이 문자열을 지속적으로 추가해야 하는 경우에는 StringBuilder가 유일한 선택입니다.

이 외에 StringBuilder의 thread-safe 버전인 StringBuffer도 있습니다만, ps에서는 멀티스레드 환경을 고려하지 않으므로 제외하였습니다.


String

String은 immutable이므로, 아래 모든 함수는 string을 직접 변경하는 것이 아닌 변경된 새로운 문자열을 반환합니다.


init (CharArray)

char 배열로 문자열을 생성합니다.

1
2
val str = String(CharArray(10) { if(it % 2 == 0) '0' else '1' })
print(str) // 0101010101


length

문자열의 길이를 반환합니다.

1
val length: Int
1
2
val string = "Hello World!"
print(string.length) // 12


get

특정 인덱스의 문자를 반환합니다.

1
fun get(index: Int): Char
1
2
3
val string = "Hello World!"
println(string.get(0)) // H
println(string[0]) // H


plus

특정 문자열을 이어붙인 문자열을 반환합니다.

1
operator fun plus(other: Any?): String
1
2
val string = "Hello" + " World!"
print(string) // Hello World!


indices

문자열의 인덱스 범위를 반환합니다.

1
val CharSequence.indices: IntRange
1
print("Hello World!".indices) // 0..11


lastIndex

문자열의 마지막 인덱스를 반환하고, 비어있을 경우 -1을 반환합니다.

1
val CharSequence.lastIndex: Int
1
print("Hello World!".lastIndex) // 11


all

문자열의 모든 문자들이 주어진 조건을 만족하는지 체크합니다.

1
fun CharSequence.all(predicate: (Char) -> Boolean): Boolean
1
print("Hello World!".all { it == 'H' }) // false


any

문자열의 모든 문자들 중 하나라도 주어진 조건을 만족하는지 체크합니다.

1
fun CharSequence.any(predicate: (Char) -> Boolean): Boolean
1
print("Hello World!".any { it == 'H' }) // true


chunked

문자열을 특정 길이만큼 쪼개어 리스트로 반환합니다.

1
fun CharSequence.chunked(size: Int): List<String>
1
print("Hello World!".chunked(5)) // [Hello,  Worl, d!]


commonPrefixWith

특정 문자열과 가장 길게 일치하는 접두사를 반환하고, 존재하지 않을 경우 빈 문자열을 반환합니다.

1
2
3
4
fun CharSequence.commonPrefixWith(
    other: CharSequence,
    ignoreCase: Boolean = false
): String
1
print("HelloWorld!".commonPrefixWith("HelloKotlin!")) // Hello


commonSuffixWith

특정 문자열과 가장 길게 일치하는 접미사를 반환하고, 존재하지 않을 경우 빈 문자열을 반환합니다.

1
2
3
4
fun CharSequence.commonSuffixWith(
    other: CharSequence,
    ignoreCase: Boolean = false
): String
1
print("ByeKotlin!".commonSuffixWith("HelloKotlin!")) // Kotlin!


contains

특정 문자열 및 문자가 포함되어 있는지 체크합니다.

1
2
3
4
5
6
7
8
9
operator fun CharSequence.contains(
    other: CharSequence,
    ignoreCase: Boolean = false
): Boolean

operator fun CharSequence.contains(
    char: Char,
    ignoreCase: Boolean = false
): Boolean
1
2
print("Hello" in "Hello World!") // true
print('H' in "Hello World!") // true


count

특정 조건을 만족하는 문자의 갯수를 반환합니다.

1
fun CharSequence.count(predicate: (Char) -> Boolean): Int
1
print("Hello World!".count { it == 'o' }) // 2


drop

앞에서 특정 갯수만큼의 문자를 제거합니다.

1
fun String.drop(n: Int): String
1
print("Hello World!".drop(6)) // World!


dropWhile

특정 조건을 만족하지 않는 문자가 등장할 때 까지 앞에서부터 제거합니다.

1
fun String.dropWhile(predicate: (Char) -> Boolean): String
1
print("aaabacd".dropWhile{ it == 'a' }) // bacd


dropLast

뒤에서 특정 갯수만큼의 문자를 제거합니다.

1
fun String.dropLast(n: Int): String
1
print("Hello World!".dropLast(7)) // Hello


dropLastWhile

특정 조건을 만족하지 않는 문자가 등장할 때 까지 뒤에서부터 제거합니다.

1
fun String.dropLastWhile(predicate: (Char) -> Boolean): String
1
print("abcdcddd".dropLastWhile{ it == 'd' }) // abcdc


endsWith

문자열이 특정 문자 또는 문자열로 끝나는지 체크합니다.

1
2
3
4
5
6
7
8
9
fun CharSequence.endsWith(
    char: Char,
    ignoreCase: Boolean = false
): Boolean

fun CharSequence.endsWith(
    suffix: CharSequence,
    ignoreCase: Boolean = false
): Boolean
1
2
println("Hello World!".endsWith('!')) // true
println("Hello World!".endsWith("World!")) // true


filter

특정 조건을 만족하는 문자들로만 이루어진 문자열을 반환합니다.

1
fun String.filter(predicate: (Char) -> Boolean): String
1
print("Hello World!".filter { it.isLetter() }) // HelloWorld


filterNot

특정 조건을 만족하지 않는 문자들로만 이루어진 문자열을 반환합니다.

1
fun String.filterNot(predicate: (Char) -> Boolean): String
1
print("0123abcd".filterNot { it.isDigit() }) // abcd


filterIndexed

filter와 동일하나, index도 인자로 포함합니다.

1
2
3
fun String.filterIndexed(
    predicate: (index: Int, Char) -> Boolean
): String
1
print("01234".filterIndexed { index, c -> index == c.digitToInt() }) // 01234


first

첫번째 문자를 반환하거나, 특정 조건을 만족하는 첫번째 문자를 반환합니다.

1
2
3
4
5
fun CharSequence.first(): Char

fun CharSequence.first(
    predicate: (Char) -> Boolean
): Char
1
2
print("0123abcd".first()) // 0
print("0123abcd".first { it.isLetter() }) // a


fold

초기값에 문자에 대한 특정 함수 수행 결과를 순차적으로 누적합니다.

1
2
3
4
fun <R> CharSequence.fold(
    initial: R,
    operation: (acc: R, Char) -> R
): R
1
print("01234".fold(0) { acc, c -> acc + c.digitToInt() }) // 10


foldRight

fold와 동일하나, 역순으로 누적합니다.

1
2
3
4
fun <R> CharSequence.foldRight(
    initial: R,
    operation: (acc: R, Char) -> R
): R
1
print("01234".foldRight(0) { acc, c -> acc + c.digitToInt() }) // 10


foldIndexed

fold와 동일하나, 인자에 index를 포함합니다.

1
2
3
4
fun <R> CharSequence.foldIndexed(
    initial: R,
    operation: (index: Int, acc: R, Char) -> R
): R
1
print("01234".foldIndexed(0) { idx, acc, c -> acc + c.digitToInt() * idx }) // 30


foldRightIndexed

foldIndexed와 동일하나, 역순으로 누적합니다.

1
2
3
4
fun <R> CharSequence.foldRightIndexed(
    initial: R,
    operation: (index: Int, acc: R, Char) -> R
): R
1
print("01234".foldRightIndexed(0) { idx, acc, c -> acc + c.digitToInt() * idx }) // 30


forEach

각 문자에 대해 반복문을 실행합니다.

1
fun CharSequence.forEach(action: (Char) -> Unit)
1
2
3
4
"Hello World!".forEach {
    print(it)
}
// Hello World!


forEachIndexed

forEach와 동일하나, 인자에 index를 포함합니다.

1
2
fun CharSequence.forEachIndexed(
    action: (index: Int, Char) -> Unit)
1
2
3
4
5
6
7
"ABCD".forEachIndexed { idx, c ->
    println("$idx : $c")
}
// 1 : A
// 2 : B
// 3 : C
// 4 : D


ifBlank

특정 문자열이 비어있거나 공백만 포함할 시 기본값을 반환합니다.

1
2
3
fun <C, R> C.ifBlank(
    defaultValue: () -> R
): R where C : CharSequence, C : R
1
print("  ".ifBlank { "Hello World!" }) // Hello World!


ifEmpty

특정 문자열이 비어있을 시 기본값을 반환합니다.

1
2
3
fun <C, R> C.ifEmpty(
    defaultValue: () -> R
): R where C : CharSequence, C : R
1
print("".ifEmpty { "Hello World!" }) // Hello World!


indexOf

특정 문자 또는 문자열과 일치하는 패턴의 첫번째 인덱스를 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
fun CharSequence.indexOf(
    char: Char,
    startIndex: Int = 0,
    ignoreCase: Boolean = false
): Int

fun CharSequence.indexOf(
    string: String,
    startIndex: Int = 0,
    ignoreCase: Boolean = false
): Int
1
print("Hello World!".indexOf("World!")) // 6


indexOfAny

특정 배열 안의 문자 및 문자열과 일치하는 패턴의 첫번째 인덱스를 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
fun CharSequence.indexOfAny(
    chars: CharArray,
    startIndex: Int = 0,
    ignoreCase: Boolean = false
): Int

fun CharSequence.indexOfAny(
    strings: Collection<String>,
    startIndex: Int = 0,
    ignoreCase: Boolean = false
): Int
1
print("Hello World!".indexOfAny(setOf('!', '?', '.'))) // 11


indexOfFirst

특정 조건을 만족하는 첫번째 문자의 인덱스를 반환합니다.

1
2
3
fun CharSequence.indexOfFirst(
    predicate: (Char) -> Boolean
): Int
1
print("0123abcd".indexOfFirst { it.isLetter() }) // 4


indexOfLast

특정 조건을 만족하는 마지막 문자의 인덱스를 반환합니다.

1
2
3
fun CharSequence.indexOfLast(
    predicate: (Char) -> Boolean
): Int
1
print("0123abcd".indexOfLast { it.isDigit() }) // 3


isEmpty & isNotEmpty

비어있는 문자열인지 아닌지 체크합니다.

1
2
fun CharSequence.isEmpty(): Boolean
fun CharSequence.isNotEmpty(): Boolean
1
2
print("".isEmpty()) // true
print("".isNotEmpty()) // false


isNotBlank

공백으로만 이루어진 문자열이 아닌지 체크합니다.

1
fun CharSequence.isNotBlank(): Boolean
1
print(" ".isNotBlank()) // false


last

마지막 문자를 반환하거나, 특정 조건을 만족하는 마지막 문자를 반환합니다.

1
2
fun CharSequence.last(): Char
fun CharSequence.last(predicate: (Char) -> Boolean): Char
1
2
print("Hello World!".last()) // !
print("Hello World!".last { it.isLetter() }) // d


lowercase

문자열의 모든 알파벳 문자를 소문자로 변환한 새로운 문자열을 반환합니다.

1
fun String.lowercase(locale: Locale): String
1
print("Hello World!".lowercase()) // hello world!


map

문자열의 모든 문자에 대해 특정 연산을 거친 문자로 이루어진 리스트를 반환합니다.

1
fun <R> CharSequence.map(transform: (Char) -> R): List<R>
1
print("Hello World!".map { it.lowercaseChar() }) // [h, e, l, l, o,  , w, o, r, l, d, !]


mapIndexed

map과 동일하나, 인자로 index를 포함합니다.

1
2
3
fun <R> CharSequence.mapIndexed(
    transform: (index: Int, Char) -> R
): List<R>
1
print("Hello".mapIndexed { idx, c -> idx }) // [0, 1, 2, 3, 4]


padStart & padEnd

문자열의 시작 또는 끝에 패딩 문자를 추가합니다.

1
2
fun String.padStart(length: Int, padChar: Char = ' '): String
fun String.padEnd(length: Int, padChar: Char = ' '): String
1
2
3
4
5
6
7
8
val padWithSpace = "125".padStart(5)
println("'$padWithSpace'") // '  125'

val padWithChar = "a".padEnd(5, '.')
println("'$padWithChar'") // 'a....'

val noPadding = "abcde".padEnd(3)
println("'$noPadding'") // 'abcde'


removePrefix

문자열의 앞부분에서 특정 문자열과 일치하는 부분을 제거한 새로운 문자열을 반환합니다. 없을 경우 그대로 반환합니다.

1
fun String.removePrefix(prefix: CharSequence): String
1
print("Hello World!".removePrefix("Hello ")) // World!


removeRange

특정 범위만큼을 제거한 새로운 문자열을 반환합니다.

1
2
3
4
5
6
fun String.removeRange(
    startIndex: Int,
    endIndex: Int
): String

fun String.removeRange(range: IntRange): String
1
print("Hello World!".removeRange(0..5)) // World!


removeSuffix

문자열의 뒷부분에서 특정 문자열과 일치하는 부분을 제거한 새로운 문자열을 반환합니다. 없을 경우 그대로 반환합니다.

1
fun String.removeSuffix(suffix: CharSequence): String
1
print("Hello World!".removeSuffix(" World!")) // Hello


removeSurrounding

문자열이 인자로 넘긴 prefixsuffix가 각각 접두사, 접미사와 모두 일치할 경우에만 이를 제거한 새로운 문자열을 반환합니다. 그렇지 않을 시 그대로 반환합니다.

1
2
3
4
fun String.removeSurrounding(
    prefix: CharSequence,
    suffix: CharSequence
): String
1
print("[Hello World!]".removeSurrounding("[", "]")) // Hello World!


repeat

문자열 패턴을 반복한 문자열을 반환합니다.

1
fun CharSequence.repeat(n: Int): String
1
2
println("Word".repeat(4)) // WordWordWordWord
println("Word".repeat(0)) //


replace

문자열에서 특정 문자열 또는 정규식과 일치하는 부분을 모두 특정 문자열로 치환한 문자열을 반환합니다.

1
2
3
4
fun CharSequence.replace(
    regex: Regex,
    replacement: String
): String
1
print("Hello World!".replace("World", "Kotlin")) // Hello Kotlin!


replaceFirst

replace와 동일하나, 첫번째로 일치하는 부분만 치환한 문자열을 반환합니다.

1
2
3
4
fun CharSequence.replaceFirst(
    regex: Regex,
    replacement: String
): String
1
print("World World!".replaceFirst("World", "Hello")) // Hello World!


replaceRange

문자열의 특정 범위를 특정 문자열로 치환한 문자열을 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
fun CharSequence.replaceRange(
    startIndex: Int,
    endIndex: Int,
    replacement: CharSequence
): CharSequence

fun String.replaceRange(
    startIndex: Int,
    endIndex: Int,
    replacement: CharSequence
): String
1
print("Hello World!".replaceRange(6..10, "Kotlin")) // Hello Kotlin!


reversed

문자열을 뒤집은 새로운 문자열을 반환합니다.

1
fun String.reversed(): String
1
print("Hello World!".reversed()) // !dlroW olleH


slice

특정 범위 안의 문자열을 반환합니다.

1
2
fun String.slice(indices: IntRange): String
fun String.slice(indices: Iterable<Int>): String
1
print("Hello World!".slice(6..10)) // World


split

특정 문자, 문자열 또는 정규식을 기준으로 문자열을 쪼갠 리스트를 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun CharSequence.split(
    vararg delimiters: String,
    ignoreCase: Boolean = false,
    limit: Int = 0
): List<String>

fun CharSequence.split(
    vararg delimiters: Char,
    ignoreCase: Boolean = false,
    limit: Int = 0
): List<String>

fun CharSequence.split(
    regex: Regex,
    limit: Int = 0
): List<String>
1
print("Hello World!".split(' ')) // [Hello, World!]


startsWith

문자열이 특정 문자 또는 문자열로 시작하는지 체크합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun CharSequence.startsWith(
    char: Char,
    ignoreCase: Boolean = false
): Boolean

fun CharSequence.startsWith(
    prefix: CharSequence,
    ignoreCase: Boolean = false
): Boolean

fun CharSequence.startsWith(
    prefix: CharSequence,
    startIndex: Int,
    ignoreCase: Boolean = false
): Boolean
1
print("Hello World!".startsWith("Hello")) // true


substring

특정 범위 안의 문자열을 반환합니다.

1
fun String.substring(range: IntRange): String
1
print("Hello World!".substring(6..10)) // World


take

앞부터 특정 갯수만큼의 문자열을 갖는 새로운 문자열을 반환합니다.

1
fun String.take(n: Int): String
1
print("Hello World!".take(5)) // Hello


takeWhile

앞부터 특정 조건을 만족하지 않는 문자가 등장할 때까지 나온 문자들로 이루어진 문자열을 반환합니다.

1
fun String.takeWhile(predicate: (Char) -> Boolean): String
1
print("0123abcd0123".takeWhile { it.isDigit() }) // 0123


takeLast

뒤부터 특정 갯수만큼의 문자열을 갖는 새로운 문자열을 반환합니다.

1
fun String.takeLast(n: Int): String
1
print("Hello World!".takeLast(6)) // world!


takeLastWhile

뒤부터 특정 조건을 만족하지 않는 문자가 등장할 때까지 나온 문자들로 이루어진 문자열을 반환합니다.

1
fun String.takeLastWhile(predicate: (Char) -> Boolean): String
1
print("abcd0123abcd".takeLastWhile { it.isLetter() }) // abcd


toList & toSet & toSortedSet

문자열을 Char를 요소로 갖는 Collection으로 변환합니다.

1
2
3
fun CharSequence.toList(): List<Char>
fun CharSequence.toSet(): Set<Char>
fun CharSequence.toSortedSet(): SortedSet<Char>
1
2
3
print("Hello World!".toList()) // [H, e, l, l, o,  , W, o, r, l, d, !]
print("Hello World!".toSet()) // [H, e, l, o,  , W, r, d, !]
print("Hello World!".toSortedSet()) // [ , !, H, W, d, e, l, o, r]


trim

문자열 양 끝의 공백을 제거한 문자열을 반환합니다.

1
fun String.trim(): String
1
print("  Hello World!  ".trim()) // Hello World!


uppercase

문자열의 모든 알파벳을 대문자로 변환한 새로운 문자열을 반환합니다.

1
fun String.uppercase(locale: Locale): String
1
print("Hello World!".uppercase()) // HELLO WORLD!


StringBuilder

StringBuiler의 경우 String에서 사용 가능한 lowercase()uppercase()를 제외하고서는 모두 사용 가능합니다. 그 외에 추가적으로 알아둘만 한것은 추가 및 삭제와 관련된 함수입니다.


append

문자 및 문자열을 뒤에 추가합니다.

1
2
fun append(value: Char): StringBuilder
fun append(value: CharSequence?): StringBuilder
1
print(StringBuilder("Hello").run { append(" World!" ) }) // Hello World!


clear

문자열을 비웁니다.

1
fun clear(): StringBuilder
1
print(StringBuilder("Hello").run { clear() }.isEmpty()) // true


deleteAt

특정 인덱스의 문자를 제거합니다.

1
fun deleteAt(index: Int): StringBuilder
1
print(StringBuilder("Hello").run { deleteAt(0) }) // ello


insert

특정 인덱스에 특정 문자 및 문자열을 삽입합니다.

1
2
fun insert(index: Int, value: Char): StringBuilder
fun insert(index: Int, value: CharSequence?): StringBuilder
1
print(StringBuilder("HelloWorld!").run { insert(5, " ") }) // Hello World!


reverse

문자열을 뒤집습니다. (reversed()와는 다르게 실제 원본 문자열을 뒤집고 반환합니다.)

1
fun reverse(): StringBuilder
1
print(StringBuilder("Hello").run { reverse() }) // olleH


Char

부록으로 Char에 대해 유용한 STL에 관한 정리입니다.

digitToInt

0 ~ 9 사이의 Char를 숫자로 변환합니다.

1
fun Char.digitToInt(): Int
1
2
print('0'.toInt()) // 48 (deprecated, 문자의 유니코드를 반환한다)
print('0'.digitToInt()) // 0


isDigit

해당 문자가 숫자인지 확인합니다.

1
fun Char.isDigit(): Boolean
1
print('0'.isDigit()) // true


isLetter

해당 문자가 알파벳인지 확인합니다.

1
fun Char.isLetter(): Boolean
1
print('A'.isLetter()) // true


lowercaseChar

해당 문자를 소문자로 변환합니다.

1
fun Char.lowercaseChar(): Char
1
print('A'.lowercaseChar()) // a


uppercaseChar

해당 문자를 대문자로 변환합니다.

1
fun Char.uppercaseChar(): Char
1
print('a'.uppercaseChar()) // A


마치며

이상으로 String, StringBuilder, 그리고 Char에 대한 일부 STL의 정의와 예시를 정리해보았습니다. 사실 공식 문서에는 위에서 소개한 것보다 훨씬 많은 STL이 존재합니다만, PS에서 주로 사용되는 것들 위주로만 정리해보았습니다. 지속적으로 알고리즘 문제를 풀면서 본 포스트에서 놓친 유용한 STL이 있다면 지속적으로 추가할 계획입니다.

0%