Kotlin은 filter 함수와 같은 Iterable 인터페이스를 위한 확장 함수를 제공한다. filter 함수를 사용하는 예제는 다음과 같다:

fun averageNonBlankLength(strings: List<String>): Double =
    strings
        .filter { it.isNotBlank() }
        .map(String::length)
        .sum() / strings.size.toDouble()

한 가지 주목할 점은 Kotlin의 filtermap 함수는 Stream을 반환하는 Java의 Stream.filterStream.map과 달리 새로운 List를 반환한다는 점이다. 이 예제에서는 원본 리스트에서 filtermap을 호출한 결과로 메모리에 두 개의 새로운 리스트가 생성된다. 그러나 원본 리스트의 크기와 작업의 복잡성에 따라 이 오버헤드는 중요하지 않을 수 있다.

원본 리스트의 크기가 크거나 작업 체인의 복잡성이 커서 메모리 사용량이 우려되는 경우, List 대신 Sequence 타입을 사용할 수 있다. asSequence() 함수를 사용하여 리스트를 시퀀스로 변환하거나 sequenceOf(list) 함수를 사용하여 리스트에서 시퀀스를 생성할 수 있다. 아래 예제는 이전 예제와 동일한 작업을 수행하는 데 sequence 유형을 사용하는 방법을 보여준다:

fun averageNonBlankLength(strings: List<String>): Double =
    strings.asSequence()
        .filter { it.isNotBlank() }
        .map(String::length)
        .sum() / strings.size.toDouble()

이 예제에서는 asSequence() 함수를 사용하여 ListSequence로 변환한다. 그런 다음 filtermap 함수를 사용하여 이전과 동일한 작업을 수행하지만 중간 List를 생성하는 대신 각 함수 호출에서 Sequence가 반환된다.

시퀀스를 사용하면 lazy evaluation로 동작하므로 필요없는 계산을 하지 않는다. 이는 메모리 사용량을 줄이는 데 도움이 될 수 있다. 공식 문서에 나오는 예제를 보자.

Iterable:

val words = "The quick brown fox jumps over the lazy dog".split(" ")
val lengthsList = words.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:")
println(lengthsList)
filter: The
filter: quick
filter: brown
filter: fox
filter: jumps
filter: over
filter: the
filter: lazy
filter: dog
length: 5
length: 5
length: 5
length: 4
length: 4
Lengths of first 4 words longer than 3 chars:
[5, 5, 5, 4]

list-processing.png

Sequence:

val words = "The quick brown fox jumps over the lazy dog".split(" ")
//convert the List to a Sequence
val wordsSequence = words.asSequence()

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")
// terminal operation: obtaining the result as a List
println(lengthsSequence.toList())
Lengths of first 4 words longer than 3 chars
filter: The
filter: quick
length: 5
filter: brown
length: 5
filter: fox
filter: jumps
length: 5
filter: over
length: 4
[5, 5, 5, 4]

sequence-processing.png

시퀀스는 결과 데이터 집합의 크기를 미리 알 수 없는 상황이나, 작업이 복잡하고 중간 결과를 메모리에 저장할 필요가 없는 상황 등 메모리 효율성이 중요한 경우에 유용하다.