본문 바로가기
기타/코틀린

[Kotlin] Sequence vs Collections

by 창이2 2022. 7. 5.

안녕하세요.

이번 포스팅에서는 Sequence와 Collections에 대해 알아보려고 합니다.

 

Sequence는 Iterable로 구현된 Collections에서 사용하는 함수의 기능을 수행

하는 class 입니다. 다만 동작방식에서 Collection과 다른데 

 

이전 포스팅에서 list의 Collection map과 flatMap을 비교하면서 Collection은 해당 api의 연산을

모두 마친 후에 다음 결과를 보여주는걸 알 수 있는데요.

 

Sequence 는 오히려 연산을 하나씩 차례대로 진행하는 순서입니다.

공식 사이트에 있는 코드를 예시로 들어보겠습니다.

Collection의 함수는 매 api의 모든 결과를 받고 다음으로 넘기게 되어있습니다.

이 과정을 Eagerly Evaluation 이라고 합니다.

fun testCollection(){
    val words = "The quick brown fox jumps over the lazy dog".split(" ")

    /**
     * filter : filter를 통해 length>3인 목록들의 리스트를 구함
     * map : map으로 length를 담은 리스트로 변환
     * take : list[0~3]까지만 취함
     */
    val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
        .map { println("length: ${it.length}"); it.length }
        .take(4) // 4개만 선택
}

 

반면에 Sequence는  Lazily Evaluation 라고 불리는데 연산이 한번에 수행되는게 아니라

한단계씩 차례대로 수행되기 때문입니다.

이때 Sequence는 2가지 연산 타입이 있는데 하나는 중간 연산을 표현하는 intermediate operation 이고

다른 하나는 중간 연산을 트리거하는 terminal operation 이 있습니다.

terminal operation이 수행되어야 intermediate operation이 수행이 트리거되어 연산을 시작합니다.

 

아래 예시코드를 실행해 보면 Thread를 sleep하는 부분이 있는데

Sequence에 println()이 sleep 이후에 나타나는 것을 볼 수 있습니다.

알아보니 intermediate operation들은 iterator를 override만 하고 수행을 시작하진 않으며

terminal operation에서 실제 iterator들이 next()를 호출해 연산을 시작하는 것이었습니다.

 

fun testSequence() {
    val words = "The quick brown fox jumps over the lazy dog".split(" ")
    val wordsSequence = words.asSequence()

    /**
     * Sequence는 내부적으로 iterator를 호출하여 진행된다.
     *
     * 이 세 함수는 intermediate operation이라 부른다. 
     * filter : filter를 통해 length>3인 목록을 다음으로 넘긴다
     * map : 이전에 들어온 iterator를 통해 데이터를 가공하여 다음으로 넘긴다..
     * take : n => count 를 1씩 감소하면서 1개씩 원소를 가진다.
     */
    val lengthsSequence = wordsSequence.filter { println("filter: $it"); it.length > 3 }
        .map { println("length: ${it.length}"); it.length }
        .take(4)

    println("Lengths of first 4 words longer than 3 chars")

//    Thread.sleep(3000)

    /**
     * toList()는 terminate operation이라 부른다.
     * terminate operation이 불릴때 실제로 위 연산이 수행된다.
     */
    println(lengthsSequence.toList())
}

 

 

위에서 보이는 것 처럼 

Collection 의 경우 filter -> filter -> filter -> filter -> filter -> map -> map -> ...이렇게 모든연산을 마친 후 다음 연산이 시작되고

 

Sequence의 경우

filter -> map -> take -> toList() -> filter -> map -> take -> toList()  처럼 연산을 한번씩 수행하며 다음단계로 진행되는 것을 볼 수 있습니다.

 

결과적으로

Collection은 연산의 결과가 모두 적용된 새로운 리스트를 다음 연산에 쓰고 싶을때 좋고,

Sequence는 원소 하나의 결과가 빨리 나와야 할 때 좋을거 같습니다.

 

참고

https://kotlinlang.org/docs/sequences.html#sequence

 

Sequences | Kotlin

 

kotlinlang.org

https://stackoverflow.com/questions/72870954/where-is-the-trigger-point-when-using-sequence-in-kotlin

 

where is the trigger point when using sequence in kotlin?

I'm studying kotlin in official site. I understand that the sequence has two type operation(intermediate, terminal). And the intermediate operation is triggered when the terminal operation is called.

stackoverflow.com

 

'기타 > 코틀린' 카테고리의 다른 글

[Kotlin] inline과 reified  (0) 2022.07.02
[Kotlin] map, flatMap 비교  (0) 2022.06.21
코틀린 특징 및 자바와 차이점 정리(작성중)  (0) 2021.10.04
코틀린 Sealed Class와 Enum Class  (0) 2021.07.01

댓글