문자열 처리

1. 기본 문자열 함수

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

    1
    2
    
    val str = "Hello, Kotlin!"
    println(str.length)  // 14
    
  • substring(startIndex, endIndex): 문자열의 일부를 잘라내어 반환합니다.

    1
    2
    3
    
    val str = "Hello, Kotlin!"
    val subStr = str.substring(0, 5)
    println(subStr)  // Hello
    
  • plus: 문자열을 이어붙이는 함수 (연산자 +와 동일한 기능).

    1
    2
    3
    
    val greeting = "Hello"
    val name = "Kotlin"
    println(greeting.plus(", ").plus(name))  // Hello, Kotlin
    

2. 문자열 비교

  • compareTo: 문자열을 비교하여 결과를 반환 (0: 동일, 음수: 작음, 양수: 큼).

    1
    2
    3
    
    val str1 = "apple"
    val str2 = "banana"
    println(str1.compareTo(str2))  // 음수 반환 (-1)
    
  • equals: 문자열이 같은지 비교.

    1
    2
    3
    
    val str1 = "Kotlin"
    val str2 = "Kotlin"
    println(str1.equals(str2))  // true
    
  • == 연산자: 코틀린에서 문자열 비교는 == 연산자를 사용해도 동일하게 동작합니다. 이는 equals 메서드를 호출하는 것과 같습니다.

    1
    2
    3
    
    val str1 = "Kotlin"
    val str2 = "Kotlin"
    println(str1 == str2)  // true
    

3. 문자열 검색

  • contains: 문자열에 특정 문자열이 포함되어 있는지 확인.

    1
    2
    
    val str = "Hello, Kotlin!"
    println(str.contains("Kotlin"))  // true
    
  • indexOf: 특정 문자의 첫 번째 인덱스를 반환. 찾지 못하면 -1을 반환.

    1
    2
    
    val str = "Hello, Kotlin!"
    println(str.indexOf("Kotlin"))  // 7
    
  • lastIndexOf: 문자열 내에서 특정 문자의 마지막 인덱스를 반환.

    1
    2
    
    val str = "Hello, Kotlin, Kotlin!"
    println(str.lastIndexOf("Kotlin"))  // 14
    
  • startsWith / endsWith: 문자열이 특정 접두사 또는 접미사로 시작하거나 끝나는지 확인.

    1
    2
    3
    
    val str = "Hello, Kotlin!"
    println(str.startsWith("Hello"))  // true
    println(str.endsWith("Kotlin!"))  // true
    

4. 문자열 변경

  • replace: 특정 문자를 다른 문자로 변경.

    1
    2
    
    val str = "Hello, Kotlin!"
    println(str.replace("Kotlin", "World"))  // Hello, World!
    
  • replaceFirst: 첫 번째로 일치하는 문자열만 변경.

    1
    2
    
    val str = "Kotlin is Kotlin"
    println(str.replaceFirst("Kotlin", "Java"))  // Java is Kotlin
    
  • toUpperCase / toLowerCase: 문자열을 대문자 또는 소문자로 변환.

    1
    2
    3
    
    val str = "Hello, Kotlin!"
    println(str.toUpperCase())  // HELLO, KOTLIN!
    println(str.toLowerCase())  // hello, kotlin!
    

5. 문자열 분리 및 결합

  • split: 문자열을 특정 구분자를 기준으로 분리하여 리스트로 반환.

    1
    2
    3
    
    val str = "apple,banana,cherry"
    val fruits = str.split(",")
    println(fruits)  // [apple, banana, cherry]
    
  • joinToString: 리스트나 배열을 특정 구분자로 연결하여 하나의 문자열로 결합.

    1
    2
    3
    
    val fruits = listOf("apple", "banana", "cherry")
    val result = fruits.joinToString(", ")
    println(result)  // apple, banana, cherry
    

6. 문자열 반복 및 처리

  • repeat: 문자열을 지정한 횟수만큼 반복.

    1
    2
    
    val str = "Kotlin"
    println(str.repeat(3))  // KotlinKotlinKotlin
    
  • forEach: 문자열의 각 문자를 순회.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    val str = "Kotlin"
    str.forEach { char -> 
        println(char)  
    }
    // K
    // o
    // t
    // l
    // i
    // n
    

7. 문자열 트리밍

  • trim: 문자열의 양 끝에 있는 공백 제거.

    1
    2
    
    val str = "   Hello, Kotlin!   "
    println(str.trim())  // Hello, Kotlin!
    
  • trimStart / trimEnd: 시작 또는 끝의 공백만 제거.

    1
    2
    3
    
    val str = "   Hello, Kotlin!   "
    println(str.trimStart())  // "Hello, Kotlin!   "
    println(str.trimEnd())    // "   Hello, Kotlin!"
    

8. 문자열 템플릿 (String Templates)

코틀린에서 문자열 내에 변수를 쉽게 삽입할 수 있는 템플릿 기능을 제공합니다.

  • $ 기호로 변수 삽입: 문자열 내에 $를 사용하여 변수를 삽입.

    1
    2
    3
    
    val name = "Kotlin"
    val greeting = "Hello, $name!"
    println(greeting)  // Hello, Kotlin!
    
  • 표현식을 중괄호로 감싸서 삽입: 복잡한 표현식은 ${}로 감싸서 삽입.

    1
    2
    3
    
    val apples = 3
    val oranges = 5
    println("I have ${apples + oranges} fruits")  // I have 8 fruits
    

String 과 StringBuilder

1. String

  • 불변성: String불변(immutable) 객체입니다. 즉, 문자열이 한번 생성되면 그 값을 변경할 수 없습니다. 문자열을 조작하는 메서드를 호출할 때마다 새로운 문자열 객체가 생성됩니다.

    예를 들어, 문자열에 무언가를 추가하거나 변경하는 경우, 기존 문자열을 수정하는 것이 아니라 새로운 문자열을 반환합니다.

    1
    2
    3
    4
    
    val str = "Hello"
    val newStr = str + " World"
    println(newStr)  // Hello World
    println(str)     // Hello (기존 문자열은 그대로)
    
  • 성능: 문자열을 자주 변경해야 하는 상황에서 String을 사용하면, 매번 새로운 객체를 생성하기 때문에 메모리 및 성능에 영향을 줄 수 있습니다. 이런 경우는 StringBuilder를 사용하는 것이 더 적합합니다.

  • 사용 예시:

    1
    2
    3
    
    val name = "Kotlin"
    val greeting = "Hello, " + name
    println(greeting)  // Hello, Kotlin
    

2. StringBuilder

  • 가변성: StringBuilder가변(mutable) 객체입니다. 즉, 생성된 이후에도 문자열의 내용을 자유롭게 수정할 수 있습니다. 문자열을 추가하거나 삭제할 때마다 새로운 객체를 만들지 않고 내부의 버퍼를 수정하므로, 문자열을 자주 변경해야 할 때 성능 상 이점이 있습니다.

  • 성능: 문자열을 반복적으로 추가, 삭제, 변경하는 작업이 많을 경우 StringBuilder는 성능 측면에서 매우 효율적입니다. 특히 반복문 안에서 문자열을 여러 번 결합할 때는 String보다 StringBuilder를 사용하는 것이 좋습니다.

  • 사용 예시:

    1
    2
    3
    
    val builder = StringBuilder("Hello")
    builder.append(" World")
    println(builder.toString())  // Hello World
    
  • 주요 메서드:

    • append(): 문자열을 이어 붙입니다.

      1
      2
      3
      
      val builder = StringBuilder("Kotlin")
      builder.append(" is fun")
      println(builder.toString())  // Kotlin is fun
      
    • insert(): 특정 위치에 문자열을 삽입합니다.

      1
      2
      3
      
      val builder = StringBuilder("Kotlin")
      builder.insert(6, " Programming")
      println(builder.toString())  // Kotlin Programming
      
    • delete(): 문자열의 일부를 삭제합니다.

      1
      2
      3
      
      val builder = StringBuilder("Kotlin Programming")
      builder.delete(6, 17)
      println(builder.toString())  // Kotlin
      
    • reverse(): 문자열을 뒤집습니다.

      1
      2
      3
      
      val builder = StringBuilder("Kotlin")
      builder.reverse()
      println(builder.toString())  // niltoK
      

3. String과 StringBuilder의 차이점

특성StringStringBuilder
불변성(Immutable)불변 (변경 불가)가변 (내용 수정 가능)
새 객체 생성 여부변경 시마다 새로운 객체 생성내부 버퍼에서 직접 수정
성능문자열을 자주 수정하면 성능이 저하될 수 있음문자열을 자주 수정할 때 더 빠르고 효율적임
사용 용도불변 문자열이 필요한 경우 (일반 문자열 처리)자주 수정되는 문자열이 필요한 경우 (문자열 결합, 반복)
스레드 안전성불변이므로 스레드 안전스레드 안전하지 않음 (StringBuffer는 스레드 안전)

4. 언제 사용해야 하는가?

  • String 사용: 문자열이 변경되지 않거나 자주 변경되지 않을 경우 String을 사용하는 것이 적합합니다. 예를 들어, 상수 문자열이나 간단한 텍스트 처리에는 String을 사용합니다.

  • StringBuilder 사용: 문자열을 자주 변경하거나 반복적으로 추가, 삭제, 결합 등의 작업을 해야 하는 경우에는 StringBuilder를 사용하는 것이 성능 면에서 유리합니다. 특히, 반복문 내에서 문자열을 여러 번 수정할 때 StringBuilder가 더 효율적입니다.

결론

  • String 은 불변성이 보장되며, 문자열을 수정할 필요가 없는 경우나 간단한 문자열 처리에 적합합니다.
  • StringBuilder 는 문자열을 빈번하게 수정하거나 결합하는 경우 성능 이점을 제공합니다.

list.sort( ) 와 sorted(list)

1. .sort()

  • 리스트를 직접 변경하는 함수입니다. 즉, 원본 리스트제자리에서(in-place) 정렬합니다.
  • MutableList에서만 사용할 수 있습니다.
  • 반환값이 없으며, 정렬된 상태로 리스트를 변경합니다.
1
2
3
val mutableList = mutableListOf(3, 1, 4, 1, 5, 9)
mutableList.sort()
println(mutableList)  // [1, 1, 3, 4, 5, 9]

2. sorted()

  • 새로운 정렬된 리스트를 반환하는 함수입니다. 즉, 원본 리스트는 변경되지 않고, 정렬된 새 리스트를 반환합니다.
  • ListMutableList 모두에서 사용할 수 있습니다.
  • 불변 리스트(immutable list) 또는 변경 불가능한 리스트를 정렬할 때 유용합니다.
1
2
3
4
val list = listOf(3, 1, 4, 1, 5, 9)
val sortedList = list.sorted()
println(sortedList)  // [1, 1, 3, 4, 5, 9]
println(list)  // [3, 1, 4, 1, 5, 9] (원본 리스트는 변경되지 않음)

차이점 요약:

특성.sort()sorted()
리스트 변경 여부원본 리스트를 직접 변경원본 리스트는 그대로 두고, 새로운 리스트 반환
사용 가능한 리스트 타입MutableList에서만 사용 가능ListMutableList 모두 사용 가능
반환 값반환 값 없음 (Unit)정렬된 새로운 리스트 반환

언제 사용할까?

  • .sort(): 이미 MutableList가 있고, 그 리스트를 바로 정렬하고자 할 때 사용합니다. 원본 리스트가 필요 없고, 즉시 정렬된 리스트로 사용하려는 경우 적합합니다.

  • sorted(): 원본 리스트를 유지하면서 새로운 정렬된 리스트가 필요한 경우 사용합니다. 불변 리스트(List)를 다룰 때 적합하며, sorted()는 원본 데이터를 변경하지 않고 안전하게 사용할 수 있습니다.

null 의 의미

개발에서 null 이라는 것은 “값이 없음” 또는 “정의되지 않은 상태” 를 의미합니다. 이는 특정 변수나 객체가 어떤 값을 가지고 있지 않음을 나타내는 값입니다. 하지만 단순히 값이 없는 상태를 의미하는 것 이상의 개념을 가지고 있으며, 개발에서 여러 가지 의미를 내포할 수 있습니다.

  • null값이 없음, 정의되지 않음, 또는 적용 불가능한 상태를 나타내며, 다양한 맥락에서 사용됩니다.

  • 이는 프로그래밍에서 결여된 정보를 명시적으로 나타내는 방식이며, 잘못된 참조로 인해 예외를 발생시키거나 시스템 오류를 유발할 가능성이 있습니다.

1. 값이 없음 (Absence of a Value)

  • 설명: null은 특정 변수나 필드가 값을 가지고 있지 않음명시적으로 나타냅니다.

  • 의미: 해당 변수가 초기화되지 않았거나, 그 위치에 값이 존재하지 않는 상황을 의미합니다.

  • 예시:

    1
    
    var name: String? = null  // name 변수에 값이 없음
    
  • 실제 의미: 이 경우 변수 name에 아무 값도 할당되지 않았음을 의미합니다. 즉, 메모리 공간은 할당되었지만 해당 공간에 유효한 값은 저장되지 않은 상태입니다.

2. 초기화 되지 않음 (Uninitialized or Not Applicable)

  • 설명: 특정 변수나 객체가 아직 초기화되지 않았거나, 적용되지 않는 상태를 나타낼 때 사용됩니다.

  • 의미: 예를 들어, 어떤 데이터베이스 조회에서 값을 찾지 못했을 때, 해당 필드에 null이 반환될 수 있습니다.

  • 예시:

    1
    
    val result = database.findUserById(1)  // 사용자가 없으면 null 반환
    
  • 실제 의미: null이 반환되었다면, 데이터베이스에서 사용자 ID 1에 해당하는 데이터가 없거나, 해당 값이 아직 설정되지 않았음을 의미합니다.

3. 알 수 없는 상태 (Unknown State)

  • 설명: 특정 정보가 알 수 없는 상태에 있을 때도 null이 사용됩니다. 이 경우 해당 값이 존재하지 않는 것이 아니라, 현재 값을 알 수 없거나 결정되지 않은 상태를 나타냅니다.

  • 의미: 아직 값을 제공하지 않은 경우에 사용될 수 있으며, 그 값이 나중에 설정될 가능성이 있습니다.

  • 예시:

    1
    
    var score: Int? = null  // 아직 점수가 입력되지 않음
    
  • 실제 의미: score는 아직 입력되지 않았지만, 나중에 점수가 입력될 수 있음을 의미합니다.

4. 오류 가능성 또는 예외 처리 필요 (Error Handling)

  • 설명: null 값은 오류 또는 예외 상황을 나타낼 수 있습니다. 특히, 값이 예상대로 설정되지 않았거나, 실패한 작업을 나타낼 때 null이 사용될 수 있습니다.

  • 의미: 이 경우 null이 함수나 메서드의 실패를 의미할 수 있으며, 이를 처리해야 하는 로직이 필요합니다.

  • 예시:

    1
    
    val connection = database.getConnection() ?: throw Exception("Connection failed")
    
  • 실제 의미: 데이터베이스 연결이 실패했을 때, null이 반환되고 예외를 발생시켜야 하는 상황을 나타냅니다.

5. 의도적 비활성화 (Intentional Absence)

  • 설명: 개발자가 특정 값이 의도적으로 비활성화되었음을 나타내기 위해 null을 사용하기도 합니다. 즉, 값이 없다는 것이 명시적으로 선택된 경우입니다.

  • 의미: 어떤 값이나 객체가 사용되지 않을 것임을 명시적으로 나타냅니다.

  • 예시:

    1
    
    var logger: Logger? = null  // 로깅 기능이 필요 없을 때 null
    
  • 실제 의미: 이 경우 logger 객체는 의도적으로 설정되지 않았으며, 필요할 때만 설정되거나 아예 사용되지 않을 것임을 의미합니다.