코틀린 (Kotlin) 시퀀스 사용하기(지연 계산)

지연 계산

아래 코드는 map과 filter 를 호출합니다.
그러나 map 에서 리스트를 생성하고 filter 에서도 리스트를 새로 생성하기때문에 데이터가 많은 경우 효율적이지 않습니다.

people.map(Person::name).filter { it.startsWith("A") }

시퀀스(sequence)

큰 데이터를 연산할 때는 중간 리스트를 생성하지 않는 시퀀스를 사용하는 것이 효율적입니다.

시퀀스는 아래 연산처럼 중간 연산에서는 아무것도 실행되지 않습니다.

listOf(1, 2, 3, 4)
            .asSequence()
            .map { it * it }
            .filter { it % 2 == 0 }

시퀀스는 최종 연산이 호출될 때 실행됩니다.

listOf(1, 2, 3, 4)
    .asSequence()
    .map { it * it }
    .filter { it % 2 == 0 }
    .toList() //최종 연산

시퀀스의 실행

시퀀스의 연산은 각 값에 대해 순차적으로 적용됩니다.

listOf(1, 2, 3, 4)
    .asSequence()
    .map { it * it }
    .find { it > 3 }

위 코드의 경우 실행 순서는 다음과 같습니다.
  • list(1) -> map(1*1) -> find(1>3) -> list(2) -> map(2*2) -> find(4>3) -> 종료
이처럼 시퀀스는 중간 리스트를 만들지않고 값을 순차적으로 계산하기때문에 특정 조건이 만족하는경우 중간에 이터레이션을 종료할 수 있습니다.

시퀀스가 없는 경우에는 map 리스트(1,4,9,10) 을 생성하고 map 리스트 값에서 find 로 4를 찾아 반환할 것이기 때문에 불필요한 중간 리스트가 생성됩니다.

시퀀스에서 map과 filter 순서 차이

시퀀스를 사용할 때 map과 filter 를 어떤 순서로 적용하느냐에 따라 성능이 달라질 수 있습니다.

map 다음 filter 를 사용하면 map 에서 모든 원소를 이터레이션합니다.
filter 다음 map 을 사용하면 filter 에서 filter(it.length < 4) 와 같이 조건에 만족하는 값만 이터레이션하기 때문에 더 효율적으로 사용할 수 있습니다.

시퀀스 만들기

asSequence() 없이 직접 시퀀스를 만들 수 있습니다.
generateSequence() 함수로 첫 번째 값을 지정하고 다음 원소를 구하는 식을 지정하면 됩니다.
그리고 sum(), any(), find() 등으로 최종 연산을 지정하면 원하는 값을 얻을 수 있습니다.

//sequence 는 함수를 한개 원소씩 순차적으로 실행하여 조건에 맞으면 그대로 종료
val naturalNumbers = generateSequence(0) { it + 1} //0부터 시작
//여기까지도 아무것도 실행안됨
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
//sum() 은 최종연산
numbersTo100.sum()

//시퀀스로 파일 탐색하기
fun File.isinsideHiddenDirectory() = //확장함수
    generateSequence(this) {
        it.parentFile
    }.any {
        it.isHidden
        //find 함수로 원하는 디렉터리를 찾을 수도 있음
    }

val file = File("/storage/self/primary/Download/mug_obj_154719227478915505.png")
file.isinsideHiddenDirectory()

*개인적으로 코틀린을 공부하면서 정리한 자료입니다. 수정 사항 및 이슈가 있는 경우 메일 부탁드립니다.


댓글

이 블로그의 인기 게시물

코틀린 (Kotlin) filter, map, all, any, count, find, groupBy, flatMap 함수 정리

코틀린 (Kotlin) 인터페이스 정리

RecyclerView 에서 notifyItemChanged()의 payload 이해하기