컬렉션과 배열

유우선·2026년 2월 12일

Kotlin Study📚

목록 보기
20/32

null이 될 수 있는 값의 컬렉션과 null이 될 수 있는 컬렉션

  • 인자로 쓰인 타입에도 물음표( ? )를 붙이면 null을 저장할 수 있음
fun readNumbers(text: String): List<Int?> {
    val result = mutableListOf<Int?>() // null이 될 수 있는 값으로 이뤄진 가변 리스트
    for(line in text.lineSequence()){ // 입력 문자열을 한 줄씩 순회
        val numberOrNull = line.toIntOrNull()
        result.add(numberOrNull)
    }
    return result
}
  • list<Int?> → Int? 타입을 저장할 수 있음
    • Int 또는 null을 저장할 수 있음

변수 타입의 null 가능성과 타입 파라미터의 null 가능성

  1. List<Int?>
    • 리스트 자체는 항상 null이 아님
    • 리스트에 들어가는 각 원소는 null이 될 수 있음
  2. List?
    • 리스트를 가리키는 변수에는 null이 들어갈 수 있음
    • 리스트의 각 원소에는 null이 들어갈 수 없음

변수 타입과 타입 파라미터의 null 가능성

  • 리스트를 가리키는 변수에 대한 null 검사와 원소에 대한 null 검사를 해줘야 함

filterOrNot 함수

fun addValidNumbers(numbers: List<Int?>){
    val validNumbers = numbers.filterNotNull()
    println("Sum of valid numbers: ${validNumbers.sum()}")
    println("Invalid numbers: ${numbers.size - validNumbers.size}")
}
  • null이 아닌 값들을 걸러냄


읽기 전용과 변경 가능한 컬렉션

  • 코틀린 컬렉션 → 데이터 접근 인터페이스와 데이터 변경 인터페이스를 분리함
    • kotiln.collections.Collection부터 존재
  • Collections 인터페이스
    • 원소 이터레이션
    • 컬렉션의 크기
    • 저장된 값 검사
    • 데이터 읽기
    • 원소를 추가하는 인터페이스가 없음
  • MutableCollection 인터페이스
    • Collections 인터페이스를 확장
    • 원소 추가, 삭제 등의 기능 확장

  • 인터페이스를 구분한 이유 → 프로그램에서 데이터에 어떤 일이 벌어지는지 쉽게 이해하기 위해
    • 어떤 인터페이스를 인자로 받는지에 따라 어떤 작업을 하는지 유추할 수 있음
fun <T> copyElements(source: Collection<T>, target: MutableCollection<T>) {
    for(item in source)
            target.add(item)
}

fun main() {
    val source: Collection<Int> = arrayListOf(3, 5, 7)
    val target: MutableCollection<Int> = arrayListOf(1)
    copyElements(source, target)
    print(target)
    // [1, 3, 5, 7]
}
  • 변경 가능한 컬렉션에 읽기 전용 컬렉션을 넘길 수 없음
fun main() {
    val source: Collection<Int> = arrayListOf(3, 5, 7)
    val target: Collection<Int> = arrayListOf(1)
    copyElements(source, target)
    print(target)
    // Argument type mismatch: 
    // actual type is 'Collection<Int>', but 'MutableCollection<Int>' was expected.
}

코틀린 컬렉션과 자바 컬렉션의 연관성

  • 코틀린 컬렉션 → 상응하는 자바 컬렉션 인터페이스의 인스턴스임
    • 코틀린과 자바를 오갈 때 아무 변환도 필요 없음

  • 코틀린의 컬렉션 인터페이스
    • 기본구조 → java.util 패키지의 컬렉션 인터페이스와 구조가 같음
    • 변경 가능 인터페이스 → 읽기 전용 인터페이스를 확장
  • 자바는 읽기 전용 컬렉션과 변경 가능 컬렉션을 구분하지 않음
    • 코틀린에서 읽기 전용 Collection으로 선언된 객체라도 자바 코드에서는 그 컬렉션 객체의 내용을 변경할 수 있음
  • 자바는 컬렉션에 null을 저장할 수 있음
    • 코틀린에서 null이 아닌 원소로 이뤄진 컬렉션을 자바에 넘기면 자바에서는 컬렉션에 null을 넣을 수 있음

자바에서 선언한 컬렉션은 코틀린에서 플랫폼 타입으로 보임

  • 플랫폼 타입 → 널 관련 정보가 없음
    • null이 될 수 있는 타입과 없는 타입 어느 쪽으로든 사용할 수 있음
  • 자바에서 선언한 컬렉션 → 코틀린에선 플랫폼 타입으로 보임
    • 컬렉션 타입이 시그니처에 들어간 자바 메서드 구현을 오버라이드 하려는 경우 읽기 전용 컬렉션과 변경 가능 컬렉션의 차이에 주의해야 함
    • 오버라이드 하려는 메서드의 자바 컬렉션 타입을 어떤 코틀린 컬렉션 타입으로 표현할지 결정해야 함

코틀린 컬렉션으로 변환 시 고려사항

  • 컬렉션이 null이 될 수 있는가?
  • 컬렉션의 원소가 null이 될 수 있는가?
  • 메서드가 컬렉션을 변경할 수 있는가?

예시

import java.io.File;
import java.util.List;

interface FileContentProcessor {
    void processContents(
            File path,
            byte[] binaryContents,
            List<String> textContents
    );
}
  • 일부 파일이 이진 파일이고 이진 파일의 내용을 텍스트로 표현할 수 없을 수 있으므로 리스트는 null이 될 수 있음
  • 파일의 각 줄은 null일 수 없으므로 이 리스트의 원소는 null일 수 없음
  • 파일의 내용을 표현하며 그 내용을 바꿀 필요 없으므로 읽기 전용임
import java.io.File

class FileIndexer: FileContentProcessor {
    override fun processContents(
        path: File?,
        binaryContents: ByteArray?,
        textContents: List<String?>?) 
    {
        /*...*/
    }
}

성능과 상호운용을 위해 객체 배열이나 원시 타입 배열 만들기

  • 코틀린 배열 → 타입 파라미터를 받는 클래스
    • 원소의 타입은 타입 파라미터에 의해 결정됨

코틀린에서 배열을 만드는 방법

  1. arrayOf 함수 : 인자로 받은 원소들을 포함해 배열을 만듬
  2. arrayOfNulls : 모든 원소가 null인 정해진 크기의 배열을 만듬
  3. Array 생성자 : 배열 크기와 람다를 인자로 받아 람다를 호출해 각 배열 원소를 초기화 함

Array 생성자 예제

fun main() {
    val letters = Array<String>(26) { i -> ('a' + i).toString() }
    println(letters.joinToString(""))
    // abcdefghijklmnopqrstuvwxyz
}
  • 타입 인자를 생략해도 컴파일러가 알아서 원소 타입을 추론해줌

컬렉션을 배열로 변환 (toTypedArray 메서드)

fun main() {
    val strings = listOf("a", "b", "c")
    println("%s/%s/%s".format(*strings.toTypedArray())) 
    // vararg 인자를 넘기기 위해 스프레드 연산자 사용
    // a/b/c
}

원시 타입의 배열

  • 원시 타입의 배열을 표현하는 별도 클래스를 각 원시 타입마다 하나씩 제공
    • IntArray, ByteArray, CharArray, BooleanArray 등…
    • 자바 원시 타입 배열인 int[], byte[], char[] 등으로 컴파일 됨

원시 타입 배열을 만드는 방법

  1. 각 배영 타입의 생성자 : size를 인자로 받아 해당 원시 타입의 기본값으로 초기화된 size 크기의 배열을 반환

    val fiveZeros = IntArray(5)
  2. 팩토리 함수 : 여러 값을 가변 인자로 받아 그 값이 들어간 배열을 반환

    val fiveZeros = intArrayOf(0, 0, 0, 0, 0)
  3. 크기와 람다를 인자로 받는 생성자

    fun main() {
    		val squares = IntArray(5) { i -> (i + 1) * (i + 1) }
    		println(squares.joinToString())
    		// 1, 4, 9, 16, 25
    }

원시 타입 배열 과 컬렉션 확장 함수

  • 코틀린 표준 라이브러리 → 배열 기본 연산 + 컬렉션 확장 함수 제공
    • filter, map 등을 배열에 써도 잘 작동함
fun main(args: Array<String>) {
    args.forEachIndexed { index, element -> 
        println("Argument $index is : $element")
    }
}

0개의 댓글