본문 바로가기
Language/Kotlin

코틀린의 `asSequence()`

by 시니성 2023. 8. 29.

asSequence()는 코틀린의 컬렉션에 대해 호출할 수 있는 확장 함수입니다. 이 함수는 주어진 컬렉션을 시퀀스로 변환합니다. 시퀀스는 지연 계산(lazy evaluation)의 원칙을 따르는 데이터의 연속입니다. 이 원칙 때문에 asSequence()는 중간 연산을 즉시 실행하지 않고, 최종 연산이 호출될 때까지 지연시키게 됩니다.

즉시 계산 (Eager Evaluation)

즉시 계산은 표현식이 평가될 때 즉시 결과를 반환합니다. 일반적인 컬렉션 연산은 즉시 평가됩니다. 예를 들어, 리스트에 mapfilter를 연속으로 호출하면, map이 호출될 때마다 리스트의 모든 원소를 변경하고, 그 다음 filter가 호출될 때 변경된 리스트의 모든 원소를 검사합니다.

지연 계산 (Lazy Evaluation)

지연 계산은 표현식의 결과가 필요할 때까지 평가를 지연시킵니다. 코틀린의 시퀀스는 지연 계산을 사용합니다. 이는 중간 연산(예: map, filter)이 즉시 실행되지 않고 최종 연산(예: toList, first, sum)이 호출될 때만 실행된다는 것을 의미합니다.

수평적 평가 (Horizontal Evaluation)

일반적인 컬렉션 연산은 수평적으로 평가됩니다. 이는 각 연산이 전체 컬렉션에 적용되며, 다음 연산이 이어집니다.

수직적 평가 (Vertical Evaluation)

시퀀스는 수직적 평가 방식을 따릅니다. 이는 각 원소가 모든 연산을 통해 수직적으로 처리되며, 다음 원소가 이어집니다.

시나리오

시나리오: 수천 개의 숫자가 들어있는 리스트에서 짝수만 선택한 다음, 각 숫자에 2를 곱하고, 처음 5개의 결과만 가져오고 싶습니다.

예시 코드

fun main() {
    // 큰 리스트 생성
    val numbers = List(10000) { it }

    // 일반적인 컬렉션 연산 사용
    val result1 = numbers.filter { it % 2 == 0 }.map { it * 2 }.take(5)
    println(result1)  // [0, 4, 8, 12, 16]

    // asSequence()를 사용한 최적화
    val result2 = numbers.asSequence().filter { it % 2 == 0 }.map { it * 2 }.take(5).toList()
    println(result2)  // [0, 4, 8, 12, 16]
}

주석 설명

result1에서는 리스트에 대한 filter 연산이 즉시 수행되어 짝수만을 선택하고, 그 결과에 대해 map 연산이 즉시 수행되어 각 숫자를 2배로 만듭니다. 이렇게 처리된 전체 결과에서 처음 5개의 숫자만 가져옵니다.

result2에서는 asSequence()를 사용하여 지연 계산을 활용합니다. 여기서 중요한 점은 filter, map, take와 같은 중간 연산이 즉시 실행되지 않는다는 것입니다. toList()는 최종 연산으로, 이 때 모든 중간 연산이 수행됩니다. 이 방식은 큰 리스트에서 효율적입니다.

이 예에서는 큰 차이를 느끼기 어렵지만, 매우 큰 데이터에서는 asSequence()의 사용이 중간 결과를 줄이기 때문에 메모리 사용량을 줄이고 성능을 향상시킬 수 있습니다.